Лабораторная 5.3 — RTL-смеситель IQ на основе NCO¶
Цель¶
Реализовать учебный fixed-point IQ-смеситель на Verilog с фазовым аккумулятором, таблицей sin/cos и комплексным умножением.
Что выполняется¶
В работе студент:
- изучает фазовый аккумулятор NCO;
- анализирует LUT sin/cos в формате Q1.15;
- запускает RTL-смеситель IQ;
- генерирует эталонные векторы Python-скриптом;
- проверяет 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 проверяет:
- поведение при сбросе;
- выравнивание
out_valid; - продвижение фазы только при valid на входе;
- знаковое комплексное умножение;
- округление до Q1.15;
- границы насыщения;
- отсчёт-за-отсчётом согласие с эталонными векторами 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.
- Сделайте смеситель сопряжённым (поворот в другую сторону):
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. - Замените округление усечением:
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. - Оцените динамический диапазон, свободный от паразитных составляющих (SFDR), для таблицы на 16 значений по 16 бит и сравните с правилом «около 6 дБ на бит адреса фазы», измеренным в Lab 3.3. Что бы вы увеличили в первую очередь: длину таблицы или разрядность её слов?
Ответы (сверены с RTL и стендом)
- Наблюдаемый вывод приведён в упражнении: сопряжённый смеситель переносит сигнал на −fs/16, как в Лабораторной 3.3.
- Наблюдаемый вывод приведён в упражнении. Обратите внимание на причину: Q1.15 не может представить +1,0, поэтому
cos = 32767, и усечение вместо округления превращает 11 999,63 в 11 999. - В этом 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 промышленной архитектурой генератора.