--=====================================================
-- Проект 36. Многоканальный контроллер сервоприводов
-- Один таймер периода 20 мс обслуживает четыре канала
-- ШИМ с независимой длительностью импульса.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity servo_ctrl is
    generic (
        CLK_DIV : integer := 50                          -- 50 МГц -> 1 МГц (1 мкс/такт)
    );
    port (
        clk      : in  std_logic;
        rst      : in  std_logic;
        pulse0   : in  std_logic_vector(10 downto 0);    -- длительность, мкс, 4 канала
        pulse1   : in  std_logic_vector(10 downto 0);
        pulse2   : in  std_logic_vector(10 downto 0);
        pulse3   : in  std_logic_vector(10 downto 0);
        servo_out: out std_logic_vector(3 downto 0)
    );
end entity servo_ctrl;

architecture rtl of servo_ctrl is
    signal us_tick : std_logic := '0';
    signal presc   : integer range 0 to CLK_DIV-1 := 0;
    signal period  : integer range 0 to 19999 := 0;      -- 20000 мкс = 20 мс
begin
    -- делитель до одной микросекунды
    process (clk, rst)
    begin
        if rst = '1' then
            presc   <= 0;
            us_tick <= '0';
        elsif rising_edge(clk) then
            if presc = CLK_DIV - 1 then
                presc   <= 0;
                us_tick <= '1';
            else
                presc   <= presc + 1;
                us_tick <= '0';
            end if;
        end if;
    end process;

    -- общий счётчик периода и четыре независимых компаратора
    process (clk, rst)
    begin
        if rst = '1' then
            period <= 0;
        elsif rising_edge(clk) then
            if us_tick = '1' then
                if period = 19999 then
                    period <= 0;
                else
                    period <= period + 1;
                end if;
            end if;
        end if;
    end process;

    servo_out(0) <= '1' when period < to_integer(unsigned(pulse0)) else '0';
    servo_out(1) <= '1' when period < to_integer(unsigned(pulse1)) else '0';
    servo_out(2) <= '1' when period < to_integer(unsigned(pulse2)) else '0';
    servo_out(3) <= '1' when period < to_integer(unsigned(pulse3)) else '0';
end architecture rtl;
