Перейти к содержанию

Лабораторная 5.3 — RTL-смеситель IQ на основе NCO

Цель

Реализовать учебный fixed-point IQ-смеситель на Verilog с фазовым аккумулятором, таблицей sin/cos и комплексным умножением.

Что выполняется

В работе студент:

  1. изучает фазовый аккумулятор NCO;
  2. анализирует LUT sin/cos в формате Q1.15;
  3. запускает RTL-смеситель IQ;
  4. генерирует эталонные векторы Python-скриптом;
  5. проверяет complex multiply, округление и насыщение.

Результат

После выполнения работы должны быть получены:

  • RTL-модуль nco_mixer_iq;
  • Python-generated reference vectors;
  • self-checking testbench;
  • VCD waveform;
  • PASS/FAIL лог симуляции.

Что приложить к отчёту

  • описание NCO;
  • таблицу fixed-point форматов;
  • формулы комплексного умножения;
  • результат симуляции;
  • вывод о масштабировании к реальному SDR-смесителю.

Подробная техническая часть

Исполняемый HDL-пакет

Файл Назначение
blocks/block_05_fpga_hdl_flow/rtl/nco_mixer_iq.v исполняемый RTL-блок микшера IQ на NCO в Q1.15
blocks/block_05_fpga_hdl_flow/tb/tb_nco_mixer_iq.v самопроверяющийся Verilog-testbench
blocks/block_05_fpga_hdl_flow/python/generate_nco_mixer_iq_vectors.py генератор детерминированных эталонных векторов
blocks/block_05_fpga_hdl_flow/tb/nco_mixer_iq_input_vectors.txt сгенерированные входные векторы
blocks/block_05_fpga_hdl_flow/tb/nco_mixer_iq_expected_vectors.txt сгенерированные ожидаемые выходные векторы

Запуск из корня репозитория:

python blocks/block_05_fpga_hdl_flow/python/generate_nco_mixer_iq_vectors.py

iverilog -g2012 \
  -o blocks/block_05_fpga_hdl_flow/tb/tb_nco_mixer_iq.out \
  blocks/block_05_fpga_hdl_flow/rtl/nco_mixer_iq.v \
  blocks/block_05_fpga_hdl_flow/tb/tb_nco_mixer_iq.v

vvp blocks/block_05_fpga_hdl_flow/tb/tb_nco_mixer_iq.out

Ожидаемый результат:

PASS: nco_mixer_iq test completed without errors

Workflow GitHub Actions .github/workflows/block5_hdl.yml автоматически генерирует векторы и запускает эту симуляцию.

Инженерный вопрос

Как fixed-point цифровой микшер становится тактируемым RTL-блоком с NCO, LUT, комплексным умножителем, округлением и насыщением?

Уравнение DSP

Для комплексного входа:

x[n] = I[n] + jQ[n]
w[n] = cos(phi[n]) + j sin(phi[n])
y[n] = x[n] * w[n]

RTL реализует:

y_i = I*cos - Q*sin
y_q = I*sin + Q*cos

Модель NCO

Учебный RTL использует компактный аккумулятор фазы:

phase[n+1] = phase[n] + phase_increment

Для этой работы:

PHASE_W = 4
размер LUT = 16 отсчётов
PHASE_INC = 1

Это намеренно мало, чтобы студенты могли изучить все значения LUT и переходы на waveform. Промышленному микшеру SDR нужны более широкий аккумулятор фазы и более качественная реализация генератора.

Форматы fixed-point

Сигнал Формат Комментарий
Входные I/Q Q1.15 знаковые 16-битные отсчёты
LUT sin/cos Q1.15 знаковые 16-битные значения генератора
Произведение Q2.30 результат умножения
Аккумулятор учебный 40-битный аккумулятор безопасен для суммы/разности
Выходные I/Q Q1.15 округлены и насыщены

Тракт данных RTL

flowchart LR
    PHASE[Аккумулятор фазы] --> LUT[LUT sin/cos]
    LUT --> CMUL[Комплексный умножитель]
    IN[Входные IQ] --> CMUL
    CMUL --> ROUND[Округление до Q1.15]
    ROUND --> SAT[Насыщение]
    SAT --> OUT[Выходные IQ]

Стратегия testbench

Testbench проверяет:

  1. поведение при сбросе;
  2. выравнивание out_valid;
  3. продвижение фазы только при valid на входе;
  4. знаковое комплексное умножение;
  5. округление до Q1.15;
  6. границы насыщения;
  7. отсчёт-за-отсчётом согласие с эталонными векторами Python.

Масштабирование к реальным проектам SDR

Учебная работа Промышленная версия SDR
4-битный аккумулятор фазы 24–48-битный аккумулятор фазы
LUT на 16 элементов большая LUT, интерполированная LUT или CORDIC
нет tready AXI-Stream valid/ready
компактный умножитель без конвейера конвейерный комплексный умножитель на DSP-блоках
векторный testbench ограниченная/случайная и файловая регрессия

Чего ожидать

PASS: nco_mixer_iq test completed without errors

NCO использует таблицу синуса на 16 значений и PHASE_INC = 1, поэтому смеситель поворачивает вход на +22.5 градуса на каждый валидный отсчёт (сдвиг на +fs/16). Эталон на Python использует ту же таблицу, округление и насыщение, поэтому сравнение побитовое.

Раннер курса генерирует векторы, компилирует, запускает симуляцию и считает ошибкой любую строку FAIL или ненулевой код выхода симулятора:

python tools/run_block5_hdl_smoke.py --test tb_nco_mixer_iq

На кристалле курса (out-of-context синтез в Vivado 2021.1, xc7z020clg400-2, 100 МГц; python tools/generate_block5_vivado_reports.py) этот смеситель занимает 110 LUT, 37 FF и 4 DSP48E1 и не укладывается в 100 МГц: WNS -0,807 нс, 16 из 36 конечных точек нарушают ограничение, 15 уровней логики, оценка около 92,6 МГц. Чтение таблицы, комплексное умножение, округление и насыщение выполняются за один такт: тот же шаблон, что у FIR из Lab 5.2 (что с этим шаблоном делает конвейеризация, см. её упражнение 4). После размещения и трассировки, с проверкой путей от портов (отчёт), WNS -0,361 нс (110 LUT, 43 FF, около 96,5 МГц): ближе, потому что после синтеза задержки проводов лишь оцениваются, но всё ещё не укладывается.

Упражнения

Каждое упражнение ниже: намеренная мутация RTL в одну строку. Внесите её, запустите testbench, прочитайте сообщения и верните файл (git checkout -- <file>). Приведённые выводы получены на Icarus Verilog 12.0.

  1. Сделайте смеситель сопряжённым (поворот в другую сторону): acc_i = I*cos + Q*sin, acc_q = -I*sin + Q*cos. Testbench сообщает 8 ошибок с перевёрнутым знаком Q, например out=(11087,-4592) expected=(11087,4592). Это та же ошибка знака, что в Lab 3.3: сигнал оказывается на -fs/16 вместо +fs/16.
  2. Замените округление усечением: round_q15 = value >>> SHIFT;. Testbench сообщает 8 ошибок, например out=(11999,0) expected=(12000,0). Обратите внимание: первая возникает на фазе 0, где cos = 32767, а не 32768: 12000 * 32767 / 32768 = 11999.63, что округляется до 12000, но усекается до 11999. Q1.15 не может точно представить +1.0.
  3. Оцените динамический диапазон, свободный от паразитных составляющих (SFDR), для таблицы на 16 значений по 16 бит и сравните с правилом «около 6 дБ на бит адреса фазы», измеренным в Lab 3.3. Что бы вы увеличили в первую очередь: длину таблицы или разрядность её слов?
Ответы (сверены с RTL и стендом)
  1. Наблюдаемый вывод приведён в упражнении: сопряжённый смеситель переносит сигнал на −fs/16, как в Лабораторной 3.3.
  2. Наблюдаемый вывод приведён в упражнении. Обратите внимание на причину: Q1.15 не может представить +1,0, поэтому cos = 32767, и усечение вместо округления превращает 11 999,63 в 11 999.
  3. В этом RTL фазовый аккумулятор и есть 4-битный адрес таблицы (PHASE_W = 4, PHASE_INC = 1): фаза проходит ровно все 16 элементов, ничего не усекается, и единственная ошибка это округление 16-битной амплитуды; 16-элементная 16-битная таблица при таком шаге даёт паразиты около −100 дБн, тот же результат, что случай Fs/16 Лабораторной 3.3. Цена это разрешение по частоте: доступны только кратные fs/16. Если таблицу питает более широкий аккумулятор, усечённый до 4 бит адреса, правило «6 дБ на бит адреса» предсказывает около −24 дБн. Чтобы улучшить NCO общего назначения, сначала удлиняют таблицу (больше бит адреса); 16-битные слова уже гораздо ниже паразитов усечения фазы.

Чек-лист отчёта (расширенный)

  • Указаны форматы входа, LUT, произведения, аккумулятора и выхода.
  • Объяснено поведение аккумулятора фазы.
  • Объяснено, почему LUT намеренно мала.
  • Запущен testbench и записан вывод PASS.
  • Проинспектирована waveform VCD.
  • Указана измеренная задержка.
  • Объяснено, как масштабировать проект до настоящего NCO SDR.

Шаблон инженерного вывода

Микшер NCO использует входные отсчёты Q1.15 и значения генератора Q1.15.
Учебный аккумулятор фазы — ____ бит и продвигается только при in_valid.
Выход округляется и насыщается обратно до Q1.15. Симуляция проходит относительно
эталонных векторов Python, поэтому следующий шаг — расширить аккумулятор фазы
и заменить компактную LUT промышленной архитектурой генератора.