Документация: секвенсор заряда/разряда (sequencer.vhd)

Содержание

  1. Назначение
  2. Аппаратный тракт компаратора
  3. Интерфейс
  4. Архитектура
  5. Логика режимов
  6. Статусные регистры
  7. Сброс
  8. Цепочка данных до ARM
  9. Результаты испытаний
  10. Известные ограничения
  11. Рекомендации
  12. Версия

1. Назначение

Модуль sequencer координирует работу двух импульсных генераторов (pulse_generator_core) — канала 0 (заряд) и канала 1 (разряд) — на основе сигналов двух аналоговых компараторов, контролирующих напряжение на накопительном конденсаторе.

Секвенсор реализует четыре режима работы, которые выбираются битами SEQUENCE в регистрах управления ch0_control(4) и ch1_control(4):

Режим ch0_seq ch1_seq Поведение
1 '1' '1' Совместный автоколебательный (эстафета заряд ↔ разряд)
2 '1' '0' Секвенсор только на заряде
3 '0' '1' Секвенсор только на разряде
4 '0' '0' Полностью ручной (байпас)

Секвенсор также встраивает значение компаратора в бит 5 статусного регистра, который ARM читает через AXI-Lite.

2. Аппаратный тракт компаратора

2.1. Принципиальная схема TLV3502AID (U12)

Дискретизация уровней напряжения накопителя выполняется интегральным сдвоенным компаратором TLV3502AID (корпус SOIC-8, t_pd = 4,5 нс, двухтактный Push-Pull выход). Питание микросхемы осуществляется напряжением +5 В (VLT1_5V) относительно локальной аналоговой земли VLT1_GND.

                          TLV3502AID (U12)
                                 ┌───∪───┐
         Vref_high (0.310 В) ────┤ 1   8 ├── VLT1_5V (+5 В)
                        U_in ──┬─┤ 2   7 ├── OUTA (Порог 55 В) ──> ISO7220 Pin 2
                               └─┤ 3   6 ├── OUTB (Порог 45 В) ──> ISO7220 Pin 3
          Vref_low (0.254 В) ────┤ 4   5 ├── VLT1_GND (0 В)
                                 └───────┘
Вывод Имя Назначение и подключение
1 +IN_A Опорный потенциал верхнего порога V_ref_high = 0,310 В (соответствует 55 В на накопителе)
2 -IN_A Измеряемый потенциал U_in от делителя накопителя (0,287 В при 50 В)
3 +IN_B Объединён с выводом 2, потенциал U_in
4 -IN_B Опорный потенциал нижнего порога V_ref_low = 0,254 В (соответствует 45 В на накопителе)
5 V- Потенциал аналоговой земли VLT1_GND
6 OUTB Выход дискриминатора нижнего порога (45 В)
7 OUTA Выход дискриминатора верхнего порога (55 В)
8 V+ Шина питания VLT1_5V с развязывающим конденсатором 0,1 мкФ

Измеряемый потенциал U_in подан на инвертирующие входы (-IN_A, -IN_B), поэтому выходы компараторов имеют активный низкий уровень (см. §2.2).

2.2. Передаточная характеристика и карта компараторов

Сигналы с выходов компаратора OUTA и OUTB транслируются на разъёмы ПЛИС через цифровой изолятор ISO7220MDR (согласование уровней 5 В → 3,3 В). На выводах ПЛИС (comp_high_0 — вывод N20, comp_low_0 — вывод P20) действует активный низкий уровень ('0'):

  • comp_high_0 = '0' ⟺ U ≥ порога потолка (предел заряда);
  • comp_low_0 = '0' ⟺ U ≤ порога дна (предел разряда);
  • comp_high_0 = '1' и comp_low_0 = '1' — напряжение внутри окна.
Напряжение U comp_high comp_low
U < порога дна '1' '0'
порог дна ≤ U ≤ порог потолка '1' '1'
U > порога потолка '0' '1'

Каждый компаратор равен '1', пока напряжение не вышло за его порог (для comp_high — пока U ниже потолка, для comp_low — пока U выше дна). Значения порогов задаются аппаратно потенциометрами:

  • POT0 = 0x11 → переход comp_high в районе 56,5 В;
  • POT1 = 0x0B → переход comp_low в районе 33,5 В.

Примечание: в описании задачи фигурируют «45 В» и «55 В», но фактические пороги определяются потенциометрами. Логика секвенсора не зависит от абсолютных значений — только от переходов '1' → '0'.

3. Интерфейс

3.1. Порты

Порт Направление Ширина Назначение
clk in 1 Системная тактовая частота
rst_n in 1 Сброс, активный низкий
ch0_cmd_seq in 1 Бит 4 ch0_control — SEQUENCE канала 0
ch0_cmd_run in 1 Бит 0 ch0_control — RUN канала 0
ch0_cmd_reset in 1 Бит 1 ch0_control — RESET канала 0
ch1_cmd_seq in 1 Бит 4 ch1_control — SEQUENCE канала 1
ch1_cmd_run in 1 Бит 0 ch1_control — RUN канала 1
ch1_cmd_reset in 1 Бит 1 ch1_control — RESET канала 1
comp_low_0 in 1 Компаратор дна, '0' при U ≤ порога дна
comp_high_0 in 1 Компаратор потолка, '0' при U ≥ порога потолка
ch0_gen_run out 1 RUN генератора заряда
ch0_gen_stop out 1 STOP генератора заряда (зарезервирован, всегда '0')
ch0_gen_reset out 1 RESET генератора заряда
ch1_gen_run out 1 RUN генератора разряда
ch1_gen_stop out 1 STOP генератора разряда (зарезервирован, всегда '0')
ch1_gen_reset out 1 RESET генератора разряда
ch0_status_in in 32 Статус от генератора заряда (o_status)
ch1_status_in in 32 Статус от генератора разряда (o_status)
ch0_status_out out 32 Статус в timing_controller (бит 5 = компаратор)
ch1_status_out out 32 Статус в timing_controller (бит 5 = компаратор)

4. Архитектура

4.1. Структурная схема

                  ┌──────────────────────────────────────────┐
                  │              sequencer                    │
                  │                                           │
 comp_high_0 ─────┤──► CDC ──► comp_high_sync ──┐             │
 comp_low_0  ─────┤──► CDC ──► comp_low_sync  ──┤             │
                  │                              │            │
 ch0_cmd_seq ─────┤──► CDC ──► ch0_seq_sync ────┤             │
 ch0_cmd_run ─────┤──► CDC ──► ch0_run_sync ────┤   Логика    │
 ch0_cmd_reset ───┤──► CDC ──► ch0_rst_sync ────┤   режимов   │
 ch1_cmd_seq ─────┤──► CDC ──► ch1_seq_sync ────┤             │
 ch1_cmd_run ─────┤──► CDC ──► ch1_run_sync ────┤             │
 ch1_cmd_reset ───┤──► CDC ──► ch1_rst_sync ────┤             │
                  │                              │            │
                  │                              ▼            │
                  │                    r_ch0_run / r_ch1_run   │
                  │                              │            │
 ch0_status_in ───┤──► сборка статуса ──► ch0_status_out       │
 ch1_status_in ───┤──► сборка статуса ──► ch1_status_out       │
                  │                                           │
                  └──────────────────────────────────────────┘
                              │              │
                              ▼              ▼
                     ch0_gen_run     ch1_gen_run
                     ch0_gen_reset   ch1_gen_reset
                     ch0_gen_stop    ch1_gen_stop

4.2. CDC-синхронизация

Все асинхронные входы проходят двухступенчатый синхронизатор (meta → sync) с атрибутом ASYNC_REG = "TRUE":

  • comp_high_0 → comp_high_meta → comp_high_sync
  • comp_low_0 → comp_low_meta → comp_low_sync
  • ch0_cmd_seq → ch0_seq_meta → ch0_seq_sync
  • ch0_cmd_run → ch0_run_meta → ch0_run_sync
  • ch0_cmd_reset → ch0_rst_meta → ch0_rst_sync

(аналогично для канала 1).

  • Назначение: защита от метастабильности при переходе сигналов между тактовыми доменами.
  • Задержка: 2 такта clk на каждый сигнал.

4.3. Выходные регистры

Состояние генераторов хранится в двух регистрах:

  • r_ch0_run — управление зарядом;
  • r_ch1_run — управление разрядом.

Начальные значения: r_ch0_run = '1', r_ch1_run = '0'. При rst_n = '0' оба сбрасываются в '0'.

5. Логика режимов

5.1. РЕЖИМ 1: совместный автоколебательный

Условие: ch0_seq_sync = '1' и ch1_seq_sync = '1'.

Назначение: автоматическая эстафета заряд ↔ разряд по порогам компараторов.

Логика (приоритет сверху вниз):

Условие Действие Комментарий
comp_high_sync = '0' r_ch0_run <= '0', r_ch1_run <= '1' Потолок достигнут: гасим заряд, запускаем разряд
comp_low_sync = '0' r_ch1_run <= '0', r_ch0_run <= '1' Дно достигнуто: гасим разряд, запускаем заряд
r_ch1_run = r_ch0_run r_ch0_run <= '1', r_ch1_run <= '0' Окно: оба в одинаковом состоянии (оба вкл — конфликт, оба выкл — простой) → включаем заряд
(иначе) — Окно, активен ровно один генератор → фаза сохраняется

Ключевые особенности:

  • Break-before-make: в ветках потолка и дна сначала гасится активный генератор, потом запускается неактивный.
  • Приоритет заряда в окне: при простое (оба '0') или конфликте (оба '1') включается заряд, разряд гасится.
  • Удержание фазы: если активен ровно один генератор, состояние не меняется — разряд продолжается, пока не дойдёт до дна; заряд — пока не дойдёт до потолка.

Диаграмма состояний:

         comp_high = '0'                comp_low = '0'
         (потолок)                      (дно)
              │                              │
              ▼                              ▼
        ┌───────────┐                  ┌───────────┐
        │  РАЗРЯД   │◄──── окно ──────►│   ЗАРЯД   │
        │  активен  │   (удержание)    │  активен  │
        └───────────┘                  └───────────┘
              │                              ▲
              │                              │
              └────── окно (конфликт/ ──────┘
                       простой → заряд)

5.2. РЕЖИМ 2: секвенсор только на заряде

Условие: ch0_seq_sync = '1' и ch1_seq_sync = '0'.

r_ch0_run <= ch0_run_sync and comp_high_sync;
r_ch1_run <= ch1_run_sync;
  • Канал 0 (заряд): управляется ARM через ch0_run_sync, но отсекается по потолку — при comp_high_sync = '0' заряд гасится.
  • Канал 1 (разряд): полностью под управлением ARM (ch1_run_sync), компараторы не влияют.

Применение: ручной нагрев с защитой по верхнему порогу.

5.3. РЕЖИМ 3: секвенсор только на разряде

Условие: ch0_seq_sync = '0' и ch1_seq_sync = '1'.

r_ch0_run <= ch0_run_sync;
r_ch1_run <= ch1_run_sync and comp_low_sync;
  • Канал 0 (заряд): полностью под управлением ARM.
  • Канал 1 (разряд): управляется ARM, но отсекается по дну — при comp_low_sync = '0' разряд гасится.

Применение: ручной разряд с защитой по нижнему порогу.

5.4. РЕЖИМ 4: полностью ручной

Условие: ch0_seq_sync = '0' и ch1_seq_sync = '0'.

r_ch0_run <= ch0_run_sync;
r_ch1_run <= ch1_run_sync;

Полный байпас: биты RUN от ARM транслируются на генераторы напрямую. Компараторы не участвуют.

Применение: ручное управление без автоматики.

6. Статусные регистры

6.1. Формирование

Секвенсор принимает ch0_status_in от генератора (o_status) и подменяет бит 5 на синхронизированное значение компаратора:

ch0_status_out <= ch0_status_in(31 downto 6) & comp_high_sync & ch0_status_in(4 downto 0);
ch1_status_out <= ch1_status_in(31 downto 6) & comp_low_sync  & ch1_status_in(4 downto 0);

Разбор для ch0_status_out:

Часть Биты входа Куда попадает
ch0_status_in(31 downto 6) 31..6 биты 31..6 выхода
comp_high_sync — бит 5 выхода
ch0_status_in(4 downto 0) 4..0 биты 4..0 выхода

6.2. Карта бит ch0_status (ARM читает)

Бит Источник Смысл
0 генератор running — генератор работает
1 генератор running (дубль)
2 генератор done — завершено
3 генератор bReset — флаг программного сброса
4 генератор error — ошибка (burst=0 или pulse=0)
5 секвенсор comp_high — потолок
6..31 генератор должны быть '0'

6.3. Карта бит ch1_status (ARM читает)

Бит Источник Смысл
0 генератор running
1 генератор running (дубль)
2 генератор done
3 генератор bReset
4 генератор error
5 секвенсор comp_low — дно
6..31 генератор должны быть '0'

6.4. Интерпретация компаратора через статус

ARM читает ch0_status(5) и ch1_status(5):

ch0_status(5) ch1_status(5) Напряжение
'1' '1' окно (между порогами)
'0' '1' потолок достигнут
'1' '0' дно достигнуто
'0' '0' переход или авария

7. Сброс

7.1. Аппаратный сброс (rst_n = '0')

  • все CDC-регистры обнуляются;
  • r_ch0_run и r_ch1_run сбрасываются в '0';
  • ch0_status_out и ch1_status_out принимают значение ch0_status_in/ch1_status_in с comp_*_sync = '0' в бите 5.

7.2. Программный сброс (ch0_cmd_reset)

Транслируется в генератор через ch0_gen_reset после CDC. Секвенсор не сбрасывает свои регистры r_ch0_run/r_ch1_run по этому сигналу — только транслирует.

8. Цепочка данных до ARM

pulse_generator_core.o_status
        │
        ▼
sequencer.ch0_status_in
        │  (бит 5 → comp_high_sync)
        ▼
sequencer.ch0_status_out
        │
        ▼
timing_controller.ch0_status
        │
        ▼
AXI-slave → Zynq PS → ARM

ARM читает ch0_status по адресу в AXI-пространстве timing_controller.

9. Результаты испытаний

9.1. Прогон 30 → 60 В (секвенсор только на заряде)

U, В L1 L2 ГЕН1 ГЕН2 ST1 ST2
30 1 0 RUN RUN 0x23 0x03
34 1 1 RUN RUN 0x23 0x23
… … … … … … …
54 1 1 RUN RUN 0x23 0x23
56 1 1 STOP RUN 0x00 0x23
58 0 1 STOP RUN 0x08 0x23
60 0 1 STOP RUN 0x00 0x23

Вывод: секвенсор канала 0 корректно отсекает заряд по потолку. Бит 5 статуса (ST1) = comp_high.

9.2. Прогон 60 → 30 В (секвенсор только на разряде)

U, В L1 L2 ГЕН1 ГЕН2 ST1 ST2
60 0 1 RUN RUN 0x03 0x23
56 1 1 RUN RUN 0x03 0x23
54 1 1 RUN RUN 0x23 0x23
… … … … … … …
34 1 1 RUN RUN 0x23 0x23
32 1 0 RUN STOP 0x23 0x08
30 1 0 RUN STOP 0x23 0x08

Вывод: секвенсор канала 1 корректно отсекает разряд по дну. Бит 5 статуса (ST2) = comp_low.

9.3. Ключевые наблюдения

  • CDC работает — компараторы доходят до статуса без залипаний.
  • Бит 5 статуса = компаратор — подтверждено для обоих каналов.
  • Отсечки работают — РЕЖИМ 2 по потолку, РЕЖИМ 3 по дну.
  • bReset = '1' появляется в статусе при остановке генератора (бит 3, 0x08).
  • Аппаратные пороги: потолок ~56,5 В (POT0 = 0x11), дно ~33,5 В (POT1 = 0x0B).

10. Известные ограничения

  • Нет дебаунса компараторов. Вблизи порогов возможен дребезг, который вызовет многократное переключение r_ch0_run/r_ch1_run. Для отладки переходов это не мешает, но в реальной работе требуется фильтр.
  • Бит 5 o_status генератора не задан явно. В pulse_generator_core строка o_status(31 downto 5) <= (others => '0') закомментирована. Это может дать 'U' в симуляции. Рекомендуется раскомментировать.
  • bReset в статусе. ARM видит флаг программного сброса генератора (0x08). Это не ошибка, но стоит задокументировать.
  • i_stop не используется. Вход i_stop генератора не задействован. Секвенсор подаёт '0'. Зарезервирован на будущее.
  • Бит 1 статуса — дубль running. Возможно, должен быть ready, но ready в генераторе объявлен и не используется.

11. Рекомендации

  1. Добавить дебаунс компараторов после CDC, перед логикой режимов. Логика: одиночный '0' в одном такте = дребезг; реальный переход — устойчивое состояние N тактов. Плюс заморозка до срабатывания противоположного порога.
  2. Раскомментировать обнуление старших бит в pulse_generator_core:
o_status(31 downto 5) <= (others => '0');
  1. Проверить РЕЖИМ 1 — включить оба секвенсора, проверить эстафету заряд ↔ разряд.
  2. Проверить холодный старт в окне — оба генератора выключены, напряжение между порогами → должен включиться заряд.
  3. Добавить constraints для CDC — set_false_path или set_max_delay -datapath_only для comp_* и cmd_* в XDC.

12. Версия

  • v1.0 — базовая логика без дебаунса. Подтверждена на прогонах 30→60 В и 60→30 В.
  • Следующая версия: добавить дебаунс, проверить РЕЖИМ 1.

Приложение. Исходный код (VHDL)

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity sequencer is
    port (
        -- Системная тактовая частота и сброс CDC-регистров
        clk           : in  std_logic;
        rst_n         : in  std_logic;

        -- Управление процессора: Канал 0 (Заряд)
        ch0_cmd_seq   : in  std_logic;  -- Бит 4 регистра ch0_control (SEQUENCE)
        ch0_cmd_run   : in  std_logic;  -- Бит 0 регистра ch0_control (RUN)
        ch0_cmd_reset : in  std_logic;  -- Бит 1 регистра ch0_control (RESET)

        -- Управление процессора: Канал 1 (Разряд)
        ch1_cmd_seq   : in  std_logic;  -- Бит 4 регистра ch1_control (SEQUENCE)
        ch1_cmd_run   : in  std_logic;  -- Бит 0 регистра ch1_control (RUN)
        ch1_cmd_reset : in  std_logic;  -- Бит 1 регистра ch1_control (RESET)

        -- Входные сигналы аналоговых компараторов с платы
        -- comp_high = '0' при U >= 55 В, comp_low = '0' при U <= 45 В
        -- В окне 45..55 В оба равны '1'
        comp_low_0    : in  std_logic;
        comp_high_0   : in  std_logic;

        -- Выходной интерфейс управления: pulse_generator_core_0 (Заряд)
        ch0_gen_run   : out std_logic;
        ch0_gen_stop  : out std_logic;  -- Резервная линия (фиксированный '0')
        ch0_gen_reset : out std_logic;  -- Синхронизированный сброс

        -- Выходной интерфейс управления: pulse_generator_core_1 (Разряд)
        ch1_gen_run   : out std_logic;
        ch1_gen_stop  : out std_logic;  -- Резервная линия (фиксированный '0')
        ch1_gen_reset : out std_logic;  -- Синхронизированный сброс

        -- Статусы: вход от генераторов (o_status), выход в timing_controller
        -- Бит 5 заменяется на значение компаратора:
        --   ch0_status_out(5) = comp_high_sync (потолок 55 В, управляется sequence_0)
        --   ch1_status_out(5) = comp_low_sync  (дно 45 В, управляется sequence_1)
        -- Биты 0..4 остаются от генератора (running, running, done, bReset, error)
        ch0_status_in  : in  std_logic_vector(31 downto 0);
        ch1_status_in  : in  std_logic_vector(31 downto 0);
        ch0_status_out : out std_logic_vector(31 downto 0);
        ch1_status_out : out std_logic_vector(31 downto 0)
    );
end entity sequencer;

architecture Behavioral of sequencer is

    attribute ASYNC_REG : string;

    -- CDC-регистры синхронизации входов компараторов
    signal comp_low_meta  : std_logic := '0';
    signal comp_low_sync  : std_logic := '0';
    signal comp_high_meta : std_logic := '0';
    signal comp_high_sync : std_logic := '0';

    attribute ASYNC_REG of comp_low_meta  : signal is "TRUE";
    attribute ASYNC_REG of comp_low_sync  : signal is "TRUE";
    attribute ASYNC_REG of comp_high_meta : signal is "TRUE";
    attribute ASYNC_REG of comp_high_sync : signal is "TRUE";

    -- CDC-регистры синхронизации битов SEQUENCE
    signal ch0_seq_meta   : std_logic := '0';
    signal ch0_seq_sync   : std_logic := '0';
    signal ch1_seq_meta   : std_logic := '0';
    signal ch1_seq_sync   : std_logic := '0';

    attribute ASYNC_REG of ch0_seq_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch0_seq_sync   : signal is "TRUE";
    attribute ASYNC_REG of ch1_seq_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch1_seq_sync   : signal is "TRUE";

    -- CDC-регистры синхронизации битов RUN
    signal ch0_run_meta   : std_logic := '0';
    signal ch0_run_sync   : std_logic := '0';
    signal ch1_run_meta   : std_logic := '0';
    signal ch1_run_sync   : std_logic := '0';

    attribute ASYNC_REG of ch0_run_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch0_run_sync   : signal is "TRUE";
    attribute ASYNC_REG of ch1_run_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch1_run_sync   : signal is "TRUE";

    -- CDC-регистры синхронизации битов RESET
    signal ch0_rst_meta   : std_logic := '0';
    signal ch0_rst_sync   : std_logic := '0';
    signal ch1_rst_meta   : std_logic := '0';
    signal ch1_rst_sync   : std_logic := '0';

    attribute ASYNC_REG of ch0_rst_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch0_rst_sync   : signal is "TRUE";
    attribute ASYNC_REG of ch1_rst_meta   : signal is "TRUE";
    attribute ASYNC_REG of ch1_rst_sync   : signal is "TRUE";

    -- Выходные регистры управления генераторами
    signal r_ch0_run      : std_logic := '1';
    signal r_ch1_run      : std_logic := '0';

begin

    -- Синхронизированный транзит служебных линий
    ch0_gen_reset <= ch0_rst_sync;
    ch1_gen_reset <= ch1_rst_sync;
    ch0_gen_stop  <= '0';
    ch1_gen_stop  <= '0';

    -- =====================================================================
    -- Сборка статусов: бит 5 = компаратор, остальные 31 бит - сквозь
    -- Биты 0..4 остаются от генератора (running, running, done, bReset, error)
    -- =====================================================================
    ch0_status_out <= ch0_status_in(31 downto 6) & comp_high_sync & ch0_status_in(4 downto 0);
    ch1_status_out <= ch1_status_in(31 downto 6) & comp_low_sync  & ch1_status_in(4 downto 0);

    -- Синхронизация асинхронных дискретных сигналов
    process(clk)
    begin
        if rising_edge(clk) then
            if rst_n = '0' then
                comp_low_meta  <= '0';
                comp_low_sync  <= '0';
                comp_high_meta <= '0';
                comp_high_sync <= '0';

                ch0_seq_meta   <= '0';
                ch0_seq_sync   <= '0';
                ch1_seq_meta   <= '0';
                ch1_seq_sync   <= '0';

                ch0_run_meta   <= '0';
                ch0_run_sync   <= '0';
                ch1_run_meta   <= '0';
                ch1_run_sync   <= '0';

                ch0_rst_meta   <= '0';
                ch0_rst_sync   <= '0';
                ch1_rst_meta   <= '0';
                ch1_rst_sync   <= '0';
            else
                comp_low_meta  <= comp_low_0;
                comp_low_sync  <= comp_low_meta;
                comp_high_meta <= comp_high_0;
                comp_high_sync <= comp_high_meta;

                ch0_seq_meta   <= ch0_cmd_seq;
                ch0_seq_sync   <= ch0_seq_meta;
                ch1_seq_meta   <= ch1_cmd_seq;
                ch1_seq_sync   <= ch1_seq_meta;

                ch0_run_meta   <= ch0_cmd_run;
                ch0_run_sync   <= ch0_run_meta;
                ch1_run_meta   <= ch1_cmd_run;
                ch1_run_sync   <= ch1_run_meta;

                ch0_rst_meta   <= ch0_cmd_reset;
                ch0_rst_sync   <= ch0_rst_meta;
                ch1_rst_meta   <= ch1_cmd_reset;
                ch1_rst_sync   <= ch1_rst_meta;
            end if;
        end if;
    end process;

    -- Единый синхронный процесс управления выходами генераторов
    process(clk)
    begin
        if rising_edge(clk) then
            if rst_n = '0' then
                r_ch0_run <= '0';
                r_ch1_run <= '0';
            else
                -- =================================================================
                -- РЕЖИМ 1: СОВМЕСТНЫЙ АВТОКОЛЕБАТЕЛЬНЫЙ (Оба SEQUENCE = '1')
                -- Эстафета переключения по порогам.
                -- =================================================================
                if (ch0_seq_sync = '1' and ch1_seq_sync = '1') then

                    -- Потолок (U >= 55 В): comp_high = '0'
                    -- Сначала гасим заряд, потом запускаем разряд
                    if comp_high_sync = '0' then
                        r_ch0_run <= '0';
                        r_ch1_run <= '1';

                    -- Дно (U <= 45 В): comp_low = '0'
                    -- Сначала гасим разряд, потом запускаем заряд
                    elsif comp_low_sync = '0' then
                        r_ch1_run <= '0';
                        r_ch0_run <= '1';

                    -- Окно (оба компаратора = '1'):
                    -- Если оба генератора в одинаковом состоянии
                    -- (оба выключены - простой, или оба включены - конфликт),
                    -- включаем заряд и гасим разряд.
                    -- Если активен ровно один - сохраняем фазу.
                    elsif (r_ch1_run = r_ch0_run) then
                        r_ch0_run <= '1';
                        r_ch1_run <= '0';
                    end if;

                -- =================================================================
                -- РЕЖИМ 2: СЕКВЕНСОР ТОЛЬКО НА КАНАЛЕ 0 (ЗАРЯД)
                -- Канал 1 - прямой транзит от ПО.
                -- Канал 0 запускается от ПО и отсекается по потолку 55 В.
                -- =================================================================
                elsif (ch0_seq_sync = '1' and ch1_seq_sync = '0') then
                    r_ch0_run <= ch0_run_sync and comp_high_sync;
                    r_ch1_run <= ch1_run_sync;

                -- =================================================================
                -- РЕЖИМ 3: СЕКВЕНСОР ТОЛЬКО НА КАНАЛЕ 1 (РАЗРЯД)
                -- Канал 0 - прямой транзит от ПО.
                -- Канал 1 запускается от ПО и отсекается по дну 45 В.
                -- =================================================================
                elsif (ch0_seq_sync = '0' and ch1_seq_sync = '1') then
                    r_ch0_run <= ch0_run_sync;
                    r_ch1_run <= ch1_run_sync and comp_low_sync;

                -- =================================================================
                -- РЕЖИМ 4: ПОЛНОСТЬЮ РУЧНОЙ РЕЖИМ (Оба SEQUENCE = '0')
                -- Прямой транзит битов RUN от ПО в оба генератора.
                -- =================================================================
                else
                    r_ch0_run <= ch0_run_sync;
                    r_ch1_run <= ch1_run_sync;
                end if;
            end if;
        end if;
    end process;

    -- Прямое назначение выходных регистров на ядра генераторов
    ch0_gen_run <= r_ch0_run;
    ch1_gen_run <= r_ch1_run;

end architecture Behavioral;