-------------------------------------------------------------------------------
-- Title      : Custom CorePWM
-------------------------------------------------------------------------------
-- File       : pwm_top.vhd
-- Author     : Pavan Marisetti
-- Company    : Microsemi Corporation
-- Device     : SmartFusion
-- Standard   : VHDL
-------------------------------------------------------------------------------
-- Description: This is customized corePWM for Fan control application
--              For rgister description see CorePWM Handbook
-------------------------------------------------------------------------------
-- Copyright (c) 2011   Microsemi Corporation
--                      All rights reserved.
-------------------------------------------------------------------------------
-- Revisions  : V1.0
-------------------------------------------------------------------------------


LIBRARY ieee;
   USE ieee.std_logic_1164.all;
   USE ieee.std_logic_unsigned.all;
   USE ieee.numeric_std.all;

ENTITY pwm_top IS
   GENERIC (
      PWM_NUM              : INTEGER := 4;
      APB_DWIDTH           : INTEGER := 8;
      SHADOW_REG_EN1       : INTEGER := 0;
      SHADOW_REG_EN2       : INTEGER := 0;
      SHADOW_REG_EN3       : INTEGER := 0;
      SHADOW_REG_EN4       : INTEGER := 0;
      SHADOW_REG_EN5       : INTEGER := 0;
      SHADOW_REG_EN6       : INTEGER := 0;
      SHADOW_REG_EN7       : INTEGER := 0;
      SHADOW_REG_EN8       : INTEGER := 0;
      SHADOW_REG_EN9       : INTEGER := 0;
      SHADOW_REG_EN10      : INTEGER := 0;
      SHADOW_REG_EN11      : INTEGER := 0;
      SHADOW_REG_EN12      : INTEGER := 0;
      SHADOW_REG_EN13      : INTEGER := 0;
      SHADOW_REG_EN14      : INTEGER := 0;
      SHADOW_REG_EN15      : INTEGER := 0;
      SHADOW_REG_EN16      : INTEGER := 0;
      PWM_STRETCH_VALUE1   : INTEGER := 1;
      PWM_STRETCH_VALUE2   : INTEGER := 1;
      PWM_STRETCH_VALUE3   : INTEGER := 1;
      PWM_STRETCH_VALUE4   : INTEGER := 1;
      PWM_STRETCH_VALUE5   : INTEGER := 1;
      PWM_STRETCH_VALUE6   : INTEGER := 1;
      PWM_STRETCH_VALUE7   : INTEGER := 1;
      PWM_STRETCH_VALUE8   : INTEGER := 1;
      PWM_STRETCH_VALUE9   : INTEGER := 1;
      PWM_STRETCH_VALUE10  : INTEGER := 1;
      PWM_STRETCH_VALUE11  : INTEGER := 1;
      PWM_STRETCH_VALUE12  : INTEGER := 1;
      PWM_STRETCH_VALUE13  : INTEGER := 1;
      PWM_STRETCH_VALUE14  : INTEGER := 1;
      PWM_STRETCH_VALUE15  : INTEGER := 1;
      PWM_STRETCH_VALUE16  : INTEGER := 1
   );
   PORT (
      PRESETN              : IN STD_LOGIC;
      PCLK                 : IN STD_LOGIC;
      PSEL                 : IN STD_LOGIC;
      PENABLE              : IN STD_LOGIC;
      PWRITE               : IN STD_LOGIC;
      PADDR                : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
      PWDATA               : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
      PRDATA_regif         : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
      PWM                  : OUT STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
      PWM_STRETCH          : IN STD_LOGIC_VECTOR(0 DOWNTO 0)
      );
END pwm_top;

ARCHITECTURE pwm_top OF pwm_top IS
   COMPONENT reg_if IS
      GENERIC (
         PWM_NUM              : INTEGER := 4;
         APB_DWIDTH           : INTEGER := 8;
         SHADOW_REG_EN        : STD_LOGIC_VECTOR(15 DOWNTO 0):= "0000000000000000"
      );
      PORT (
         PCLK                 : IN STD_LOGIC;
         PRESETN              : IN STD_LOGIC;
         PSEL                 : IN STD_LOGIC;
         PENABLE              : IN STD_LOGIC;
         PWRITE               : IN STD_LOGIC;
         PADDR                : IN STD_LOGIC_VECTOR(5 DOWNTO 0);
         PWDATA               : IN STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         PWM_STRETCH          : IN STD_LOGIC_VECTOR(PWM_NUM - 1 DOWNTO 0);
         PRDATA_regif         : OUT STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         period_cnt           : IN STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         sync_pulse           : IN STD_LOGIC;
         period_out_wire_o    : OUT STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         prescale_out_wire_o  : OUT STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         pwm_enable_out_wire_o: OUT STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
         pwm_posedge_out_wire_o: OUT STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1);
         pwm_negedge_out_wire_o: OUT STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1)
      );
   END COMPONENT;
   
   COMPONENT timebase IS
      GENERIC (
         APB_DWIDTH           : INTEGER := 8
      );
      PORT (
         PRESETN              : IN STD_LOGIC;
         PCLK                 : IN STD_LOGIC;
         period_reg           : IN STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         prescale_reg         : IN STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         period_cnt           : OUT STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         sync_pulse           : OUT STD_LOGIC
      );
   END COMPONENT;
   
   COMPONENT pwm_gen IS
      GENERIC (
         PWM_NUM              : INTEGER := 8;
         APB_DWIDTH           : INTEGER := 8

      );
      PORT (
         PRESETN              : IN STD_LOGIC;
         PCLK                 : IN STD_LOGIC;
         PWM                  : OUT STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
         period_cnt           : IN STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
         pwm_enable_reg       : IN STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
         pwm_posedge_reg      : IN STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1);
         pwm_negedge_reg      : IN STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1);
         sync_pulse           : IN STD_LOGIC
      );
   END COMPONENT;

   
   SIGNAL prescale_reg    : STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
   SIGNAL period_reg      : STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
   SIGNAL period_cnt      : STD_LOGIC_VECTOR(APB_DWIDTH - 1 DOWNTO 0);
   SIGNAL pwm_enable_reg  : STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
   SIGNAL pwm_posedge_reg : STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1);
   SIGNAL pwm_negedge_reg : STD_LOGIC_VECTOR(PWM_NUM * APB_DWIDTH DOWNTO 1);
   SIGNAL sync_pulse      : STD_LOGIC;
   SIGNAL PWM_int               : STD_LOGIC_VECTOR(PWM_NUM DOWNTO 1);
   SIGNAL PWM_STRETCH_VALUE_int : STD_LOGIC_VECTOR(PWM_NUM - 1 DOWNTO 0);

   CONSTANT all_ones	  : STD_LOGIC_VECTOR(511 DOWNTO 0) := (others => '1');
   CONSTANT all_zeros	  : STD_LOGIC_VECTOR(511 DOWNTO 0) := (others => '0');
   
  
   CONSTANT         SHADOW_REG_EN        : STD_LOGIC_VECTOR(15 DOWNTO 0)	:= 
														   (std_logic_vector(to_unsigned(SHADOW_REG_EN16,1)) &
      														std_logic_vector(to_unsigned(SHADOW_REG_EN15,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN14,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN13,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN12,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN11,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN10,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN9 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN8 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN7 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN6 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN5 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN4 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN3 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN2 ,1)) & 
      														std_logic_vector(to_unsigned(SHADOW_REG_EN1 ,1)) );

   

												
 FUNCTION to_stdlogic ( x : integer) return std_logic is
  variable y  : std_logic;
  begin
    if x = 0 then
      y := '0';
    else
      y := '1';
    end if;
    return y;
  end to_stdlogic;
 
BEGIN
  

   
   -- Flatten PWM_STRETCH to create PWM_STRETCH arrary 
   
   xhdl3 : IF ((PWM_NUM > 0) ) GENERATE
      PWM_STRETCH_VALUE_int(0) <= to_stdlogic(PWM_STRETCH_VALUE1);
   END GENERATE;
   xhdl4 : IF ((PWM_NUM > 1)  ) GENERATE
      PWM_STRETCH_VALUE_int(1) <= to_stdlogic(PWM_STRETCH_VALUE2);
   END GENERATE;
   xhdl5 : IF ((PWM_NUM > 2)  ) GENERATE
      PWM_STRETCH_VALUE_int(2) <= to_stdlogic(PWM_STRETCH_VALUE3);
   END GENERATE;
   xhdl6 : IF ((PWM_NUM > 3)  ) GENERATE
      PWM_STRETCH_VALUE_int(3) <= to_stdlogic(PWM_STRETCH_VALUE4);
   END GENERATE;
   xhdl7 : IF ((PWM_NUM > 4)  ) GENERATE
      PWM_STRETCH_VALUE_int(4) <= to_stdlogic(PWM_STRETCH_VALUE5);
   END GENERATE;
   xhdl8 : IF ((PWM_NUM > 5)  ) GENERATE
      PWM_STRETCH_VALUE_int(5) <= to_stdlogic(PWM_STRETCH_VALUE6);
   END GENERATE;
   xhdl9 : IF ((PWM_NUM > 6)  ) GENERATE
      PWM_STRETCH_VALUE_int(6) <= to_stdlogic(PWM_STRETCH_VALUE7);
   END GENERATE;
   xhdl10 : IF ((PWM_NUM > 7)  ) GENERATE
      PWM_STRETCH_VALUE_int(7) <= to_stdlogic(PWM_STRETCH_VALUE8);
   END GENERATE;
   xhdl11 : IF ((PWM_NUM > 8) ) GENERATE
      PWM_STRETCH_VALUE_int(8) <= to_stdlogic(PWM_STRETCH_VALUE9);
   END GENERATE;
   xhdl12 : IF ((PWM_NUM > 9) ) GENERATE
      PWM_STRETCH_VALUE_int(9) <= to_stdlogic(PWM_STRETCH_VALUE10);
   END GENERATE;
   xhdl13 : IF ((PWM_NUM > 10) ) GENERATE
      PWM_STRETCH_VALUE_int(10) <= to_stdlogic(PWM_STRETCH_VALUE11);
   END GENERATE;
   xhdl14 : IF ((PWM_NUM > 11) ) GENERATE
      PWM_STRETCH_VALUE_int(11) <= to_stdlogic(PWM_STRETCH_VALUE12);
   END GENERATE;
   xhdl15 : IF ((PWM_NUM > 12) ) GENERATE
      PWM_STRETCH_VALUE_int(12) <= to_stdlogic(PWM_STRETCH_VALUE13);
   END GENERATE;
   xhdl16 : IF ((PWM_NUM > 13) ) GENERATE
      PWM_STRETCH_VALUE_int(13) <= to_stdlogic(PWM_STRETCH_VALUE14);
   END GENERATE;
   xhdl17 : IF ((PWM_NUM > 14) ) GENERATE
      PWM_STRETCH_VALUE_int(14) <= to_stdlogic(PWM_STRETCH_VALUE15);
   END GENERATE;
   xhdl18 : IF ((PWM_NUM > 15)) GENERATE
      PWM_STRETCH_VALUE_int(15) <= to_stdlogic(PWM_STRETCH_VALUE16);
   END GENERATE;

   -- force PWM output based on PWM_STRETCH
   -- Write process parameter-based addresses
   pwm_out_mux : FOR l IN 1 TO  (PWM_NUM) GENERATE
            PWM(l) <= PWM_STRETCH_VALUE_int(l - 1) WHEN ((PWM_STRETCH(l - 1)) = '1') ELSE
                      PWM_int(l);
   END GENERATE;



   reg_if_inst : reg_if
      GENERIC MAP (
         PWM_NUM,
         APB_DWIDTH,
         SHADOW_REG_EN
      )
      PORT MAP (
         PCLK                  => PCLK,
         PRESETN               => PRESETN,
         PSEL                  => PSEL,
         PENABLE               => PENABLE,
         PWRITE                => PWRITE,
         PADDR                 => PADDR(7 DOWNTO 2),
         PWDATA                => PWDATA,
         PWM_STRETCH           => PWM_STRETCH,
         PRDATA_regif          => PRDATA_regif,
         pwm_posedge_out_wire_o=> pwm_posedge_reg,
         pwm_negedge_out_wire_o=> pwm_negedge_reg,
         prescale_out_wire_o   => prescale_reg,
         period_out_wire_o     => period_reg,
         period_cnt            => period_cnt,
         pwm_enable_out_wire_o => pwm_enable_reg,
         sync_pulse            => sync_pulse
      );
   
   

timebase_inst : timebase
      	GENERIC MAP (
      	   APB_DWIDTH
      	)
      	PORT MAP (
         PCLK          => PCLK,
         PRESETN       => PRESETN,
         prescale_reg  => prescale_reg,
         period_reg    => period_reg,
         period_cnt    => period_cnt,
         sync_pulse    => sync_pulse
      	);


   pwm_gen_inst : pwm_gen
      GENERIC MAP (
         PWM_NUM,
         APB_DWIDTH
      )
      PORT MAP (
         PCLK             => PCLK,
         PRESETN          => PRESETN,
         PWM              => PWM_int,
         period_cnt       => period_cnt,
         pwm_enable_reg   => pwm_enable_reg,
         pwm_posedge_reg  => pwm_posedge_reg,
         pwm_negedge_reg  => pwm_negedge_reg,
         sync_pulse       => sync_pulse
      );
   
END pwm_top;




