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

entity digital_loop_filter is
    generic (
        OUT_WIDTH : integer := 16;   -- Bit width for the ADC/VCO control
        KP        : integer := 8192;  -- Proportional Gain (Sets the Zero)
        KI        : integer := 6;   -- 6Integral Gain (Sets the Loop Bandwidth)
        LPF_SHIFT : integer := 5     -- 2^-9 shift approx 93kHz bandwidth @ 300MHz
    );
    port (
        clk      : in  std_logic;
        rst_n    : in  std_logic;    -- Active-low reset
        pfd_up   : in  std_logic;    -- Up pulse from PFD
        pfd_dn   : in  std_logic;    -- Down pulse from PFD
        ctrl_out : out signed(OUT_WIDTH-1 downto 0)
    ) ;
end entity;

architecture rtl of digital_loop_filter is
    -- Internal registers for the PI section
    signal integrator : signed(31 downto 0) := (others => '0');
    signal pi_sum     : signed(31 downto 0) := (others => '0');

    -- Internal register for the Low-Pass Pole (The "Slow" Pole)
    signal lpf_acc    : signed(31 downto 0) := (others => '0');

begin

    process(clk)

        variable err_v   : signed(3 downto 0); 
        variable prop_v  : signed(31 downto 0);
        variable integ_v : signed(31 downto 0);
    begin
        if rising_edge(clk) then
            if rst_n = '1' then
                integrator <= (others => '0');
                pi_sum     <= (others => '0');
                lpf_acc    <= (others => '0');
            else
                -- 1. Determine PFD Error (-1, 0, or 1)
                if pfd_up = '1' and pfd_dn = '0' then
                    err_v := to_signed(1, 4);
                elsif pfd_dn = '1' and pfd_up = '0' then
                    err_v := to_signed(-1, 4);
                else
                    err_v := to_signed(0, 4);
                end if;

                -- 2. PI Section (Integrator + Zero)
                -- We multiply small widths first, then resize to 32 bits. 
                -- This avoids the "target 32, source 64" error.
                integ_v := resize(err_v * KI, 32);
                prop_v  := resize(err_v * KP, 32);

                -- Update the integrator register
                integrator <= integrator + integ_v;

                -- pi_sum = integrator + proportional term (This creates the ZERO)
                pi_sum <= integrator + prop_v;

                -- 3. Additional Low-Pass Pole (The 100kHz Pole)
                -- Differential equation: y[n] = y[n-1] + alpha * (x[n] - y[n-1])
                -- Shift right by 9 is equivalent to multiplying by 1/512 (alpha)
                lpf_acc <= lpf_acc + shift_right(pi_sum - lpf_acc, LPF_SHIFT);

            end if;
        end if;
    end process;

    -- Map the top bits of the LPF accumulator to the output

    ctrl_out <= lpf_acc(31 downto 32-OUT_WIDTH);

end architecture;