PTP сервер дома. Часть 7

PTP сервер дома. Часть 7

Семён сохраняет полезное_)

Всем привет! Расписал подробнее как пока вижу архитектуру что будет жить на FPGA

1. Clock & Reset Manager (clock_reset.v)

Что делает:

Создаёт стабильный глобальный тактовый сигнал и синхронизированный reset для всей FPGA.

Входы:

osc_in — пин OCXO 25 МГц

ext_reset_n — внешний сигнал сброса (или от power-on)

Выходы:

clk_25m — основной глобальный clk (25 МГц)

rst_n_sync — 3-stage синхронизированный reset

clk_1ghz — быстрый clk для TDC (через отдельный PLL)

Реализация:

Gowin PLL IP (2 инстанса):

PLL1: 25 МГц → 25 МГц (low jitter для всей логики)

PLL2: 25 МГц → 800–1600 МГц (для TDC IOLogic, рекомендуется 1000 МГц)

Global Clock Buffer (GCLK) на выходах

Reset synchronizer (3 FF)

Ресурсы: ~15 LUT + 2 PLL

Связь с регистрами: нет (базовый модуль)

2. I2C Slave + Regfile (i2c_slave_regfile.v)

Что делает:

Полноценный I²C Slave (адрес 0x58) + большой регистровый файл 128 байт (расширенный вариант, который ты просил).

Входы:

scl, sda (пины)

Все статусы от остальных модулей

Выходы:

sda_out (open-drain)

Все 128 регистров (0x00–0x7F)

Реализация:

Gowin IP Core I2C_SLAVE (IPUG504) + собственный регистровый файл на массиве reg [7:0] regs[0:127]

Автоматический little-endian для 16/32/64-бит

Автосброс бита START_TDC_CAL после запуска

Поддержка repeated start и interrupt

Ресурсы: ~700–900 LUT

Связь: все регистры (STATUS, CONTROL, PHASE_ERROR_PS, DEVICE_DNA, TEMPERATURE_C и т.д.)

3. PPS Validator with TDC (pps_validator_tdc.v) — два инстанса (GNSS + DS)

Что делает:

Синхронизирует сырой PPS, измеряет период с точностью ~25–40 пс, проверяет допуск и считает джиттер.

Входы:

pps_raw (прямой пин от GNSS или DS3231)

tdc_lsb_ps_cal (из самокалибровки)

Выходы:

pps_ok

period_ps (32 бит)

jitter_ps (16 бит)

tdc_raw (32 бит)

Реализация:

Gowin_TDC IP (один на канал)

Double FF синхронизатор

Логика расчёта периода и moving-average jitter (за 8 периодов)

Ресурсы: ~350 LUT + 1 TDC IP на один канал

Регистры: GNSS_PERIOD_PS / DS_PERIOD_PS, JITTER_GNSS_PS / JITTER_DS_PS, GNSS_TDC_RAW / DS_TDC_RAW

4. Reference Selector FSM (ref_selector_fsm.v)

Что делает:

Принимает окончательное решение, какой PPS подавать на AD9545 (GNSS > DS3231 > Holdover).

Входы:

gnss_valid_mcu, ds_valid_mcu (из регистров 0x02/0x03)

gnss_pps_ok, ds_pps_ok

force_* биты из CONTROL

phase_error_ps (для hitless)

Выходы:

selected (2 бит: 00=GNSS, 01=DS, 10=Holdover)

holdover_flag

ref_switch_count (счётчик)

Реализация:

5-состояний FSM (с hysteresis 5–10 с)

Таймер потери сигнала

Приоритет force от MCU

Ресурсы: ~450 LUT

Регистры: SELECTED_GNSS / SELECTED_DS / HOLDOVER_ACTIVE / REF_SWITCH_COUNT

5. PPS Mux + Glitch-Free Programmable Delay (pps_mux_glitchfree.v)

Что делает:

Выбирает нужный PPS и добавляет точную задержку (0–~3.2 нс с шагом ~12.5 пс) без глитчей.

Входы:

selected (из FSM)

delay_steps (из регистра 0x20)

pps_gnss_sync, pps_ds_sync

Выход:

pps_out_to_ad9545 (пин REFA AD9545)

Реализация:

2:1 mux

ODDR (для чистого фронта)

IODELAY primitive (динамическое обновление только когда PPS=0)

Защита от глитча при смене delay

Ресурсы: ~120 LUT + ODDR + IODELAY

Регистр: DELAY_STEPS

6. Phase Monitor with TDC (phase_monitor_tdc.v)

Что делает:

Измеряет фазовую ошибку между выбранным PPS и feedback PPS из AD9545 с точностью десятков пикосекунд.

Входы:

pps_selected (уже с задержкой из модуля 5)

feedback_pps (пин из AD9545)

Выходы:

phase_error_ps (signed 32 бит)

phase_alarm

Реализация:

Два Gowin_TDC (для selected и feedback)

Расчёт разницы + alarm

Ресурсы: ~400 LUT + 2 TDC IP

Регистры: PHASE_ERROR_PS, PHASE_ALARM, FEEDBACK_TDC_RAW

7. Self_Calibration_TDC (self_cal_tdc.v)

Что делает:

Автоматически калибрует реальное значение LSB TDC (пс на счёт) с помощью эталонного 1 Гц от делителя.

Входы:

start_cal (бит из CONTROL 0x01)

Выходы:

lsb_ps_calibrated

cal_done, cal_error

Реализация:

Делитель clk_25m → 1 Гц

Отдельный TDC + накопитель за 60 секунд

Формула: lsb_ps = 1_000_000_000 / (среднее_количество_тактов)

Ресурсы: ~300 LUT + 1 TDC IP

Регистры: TDC_LSB_PS_CAL, CAL_STATUS, START_TDC_CAL

8. ADC & Temperature Reader (adc_reader.v)

Что делает:

Читает встроенный температурный сенсор и сенсоры напряжения Gowin.

Входы:

start_adc_read (бит из CONTROL)

Выходы:

temperature_c_x10

vcc_core_mv, vcc_io_mv

Реализация:

Gowin ADC IP (каналы: Temperature + Voltage Sensors)

Обновление каждые 200 мс

Ресурсы: ~150 LUT + 1 ADC IP

Регистры: TEMPERATURE_C, INTERNAL_VCC_CORE_mV, INTERNAL_VCCIO_mV, ADC_STATUS

9. Status / LED / IRQ Aggregator (status_led_irq.v)

Что делает:

Собирает все статусы, управляет LED и генерирует прерывание на MCU.

Выходы:

led_gnss, led_ds, led_lock, led_holdover

irq_out (на пин MCU)

Реализация:

Простая логика OR для IRQ (смена selected, phase_alarm, cal_done и т.д.)

Ресурсы: ~80 LUT

10. Top Module (gm_ptp_fpga_top.v)

Что делает:

Инстанцирует и соединяет все 9 модулей + Gowin primitives (PLL, ADC, ODDR, IODELAY, TDC).

Особенности:

Все сигналы синхронизированы на clk_25m

.sdc файл с false_path для PPS и set_clock_groups

DEVICE_DNA читается один раз при старте

Ресурсы всего проекта: ~4.5–5.5k LUT + 4–5 PLL/TDC/ADC IP (GW5A-LV25MG121 загружен на ~20–25%)

А как fpga будет принимать решение что лучше?

FPGA принимает решение о выборе источника PPS по довольно простой, но надёжной иерархической логике с несколькими уровнями защиты от ложных переключений.

Вот как это устроено на практике (в модуле ref_selector_fsm.v):

Иерархия приоритетов (от самого высокого к низкому)

Force-команды от MCU (самый высокий приоритет — override всего остального)

Если в регистре CONTROL (0x01) установлен бит FORCE_GNSS → всегда GNSS

Если FORCE_DS → всегда DS3231

Если FORCE_HOLDOVER → всегда holdover (синтезированный PPS или просто ничего не подаём на AD9545)

→ Это позволяет оператору / софту принудительно зафиксировать источник при отладке, тестировании или известной неисправности.

GNSS как основной источник (приоритет по умолчанию)

Условия, при которых выбирается GNSS:

GNSS_VALID_MCU = 1 (MCU уверен, что приёмник имеет хороший фикс)

И

GNSS_PPS_OK = 1 (TDC-валидатор подтверждает, что период 0.999–1.001 с и джиттер не слишком большой)

DS3231 как резервный источник

Выбирается только если GNSS недоступен:

GNSS_VALID_MCU = 0 ИЛИ GNSS_PPS_OK = 0 более N секунд (обычно 10–30 с)

И

DS_VALID_MCU = 1

И

DS_PPS_OK = 1

Holdover (последний уровень)

Если оба источника плохие:

GNSS_VALID_MCU = 0 или GNSS_PPS_OK = 0

И

DS_VALID_MCU = 0 или DS_PPS_OK = 0

→ переходим в holdover

Дополнительные защитные механизмы (чтобы не дёргаться)

Hysteresis / таймер потери

Источник считается потерянным только после 10–30 секунд непрерывно плохого сигнала

Это защита от кратковременных сбоев GNSS.

Debounce включения

Новый источник включается только после 3–5 секунд стабильности

Это предотвращение «прыжков» туда-сюда.

Phase continuity check

Переключение разрешено только если phase_error_ps < 50–100 нс в момент переключения

Джиттер-фильтр

Если JITTER_xxx_PS > порога (например 30–50 нс) → источник считается плохим

Sticky holdover

После входа в holdover выход возможен только по команде MCU или при возврате GNSS.

Не дёргаться обратно при кратком улучшении

Типичный сценарий переключения (пример)

Нормальная работа

GNSS_VALID_MCU = 1, GNSS_PPS_OK = 1, джиттер < 10 нс → GNSS

GNSS потерял фикс (MCU сбросил GNSS_VALID_MCU = 0)

→ FPGA ждёт 15 секунд (таймер), потом переключается на DS3231 (если DS_VALID_MCU = 1 и DS_PPS_OK = 1)

DS3231 тоже плохой (например, джиттер > 50 нс)

→ holdover (FPGA подаёт либо синтезированный PPS от OCXO, либо просто держит последний хороший фронт — зависит от реализации)

GNSS вернулся (GNSS_VALID_MCU = 1 + стабильный PPS)

→ FPGA ждёт ещё 5 секунд стабильности → возвращается на GNSS (если |phase_error| < 50 нс)

Также в дальнейшем планирую добавить

1. Weighted score (вес GNSS выше, чем DS3231)

Идея: вместо жёстких условий (GNSS или ничего) — вычисляем баллы качества для каждого источника и выбираем тот, у кого больше баллов.

Правила выбора:

если score_gnss > score_ds + hysteresis_margin (например 30–50) → GNSS

если score_ds > score_gnss + hysteresis_margin → DS3231

иначе → остаёмся на текущем источнике (или holdover, если оба < threshold)

Преимущества: плавные переходы, можно легко настраивать веса через I²C-регистры.

Реализация:

3–4 новых 16-битных регистра для весов/коэффициентов

~150–250 LUT

Обновление 1 раз в секунду

2. Allan deviation / ADEV-оценка качества источника за последние 100–1000 с

Реалистичный компромисс для FPGA:

Полный overlapping ADEV на τ = 1…1000 с требует хранения тысяч значений и вычислений O(n²) → нецелесообразно.

Предлагаемый упрощённый вариант:

Храним последние 64–256 значений fractional frequency deviation (y_k) и считаем ADEV только для фиксированных τ (например τ = 1 с, 4 с, 16 с, 64 с, 256 с).

Упрощённая формула (overlapping Allan variance для τ = n·1 с):

textσ_y²(τ = n) ≈ (1 / (2 · (M - 2n + 1))) · Σ [ (ȳ_{i+n} - ȳ_i)² ] для i = 1…(M-2n+1)

где ȳ_i — среднее y за n секунд (но поскольку PPS 1 Гц, чаще всего ȳ_i = y_i)

Ещё более простой и популярный вариант в железе — modified Allan deviation (mod ADEV) или просто RMS разностей за окно:

textadev_proxy(τ) = sqrt( 1/(M-n) · Σ_{i=1}^{M-n} (y_{i+n} - y_i)² )

где y_k = (period_k - 1_000_000_000) / 1_000_000_000 → fractional frequency deviation

Как хранить:

Кольцевой буфер на 256 элементов (BRAM или регистры)

Каждую секунду: y_new = (measured_period_ps - 1e12) / 1e12

Сдвигаем буфер, считаем adev_proxy для нескольких τ (1, 4, 16, 64, 256)

Использование в выборе:

Если adev_proxy_gnss(τ=64) < adev_proxy_ds(τ=64) × k → GNSS лучше

Порог обычно 1.5–3× (GNSS должен быть заметно лучше)

Ресурсы: 256 × 32 бит ≈ 1–2 кБ (BRAM или distributed RAM)

~400–800 LUT на вычисления (можно оптимизировать)

3. Автоматический возврат на GNSS только если джиттер < 5 нс и phase_error < 20 нс

Реализация — очень простая и полезная доработка:

В FSM добавляем состояние "prefer GNSS but wait for clean"

Почему это важно:

GNSS часто возвращается «грязным» (высокий джиттер, скачки фазы) после потери/восстановления сигнала.

Ждём, пока он действительно станет лучше DS3231

Ресурсы: + ~100–150 LUT + счётчик стабильности

4. Вход в holdover с синтезом PPS от OCXO (через счётчик 25 млн тактов)

Когда входим в holdover:

Запоминаем последний хороший period_ps (или усреднённый за 8–16 последних секунд)

Или просто используем номинал 1_000_000_000 ps

Улучшения:

Корректировать частоту по истории (например, средний period за последние 100 с)

Делать phase slew rate limit (не прыгать фазу резко при возврате GNSS)

Ресурсы: ~50–100 LUT + 25-битный счётчик



Report Page