Лабораторная 5.10 — BPSK BER верхнего уровня, готовый для Zynq¶
Цель¶
Обернуть детерминированную кадровую BPSK-цепочку в управляющий верхний уровень, который намного ближе к будущему пути интеграции с Zynq:
start -> источник бит кадра -> кадровая цепочка TX -> внешняя граница отсчётов TX
-> внешняя граница отсчётов RX -> восстановление RX -> счётчик BER -> done
Работа оставляет источник BPSK-кадра и проверку BER внутри логики, обращённой к FPGA, и выводит наружу границу TX/RX в домене отсчётов для будущего подключения AD9363, FIFO или DMA.
Исполняемый HDL-пакет¶
| Файл | Назначение |
|---|---|
blocks/block_05_fpga_hdl_flow/rtl/bpsk_frame_bit_source.v |
детерминированный источник кадра на ROM с семантикой start/busy/done |
blocks/block_05_fpga_hdl_flow/rtl/bpsk_ber_counter.v |
сравнивает восстановленные биты с тем же ROM кадра и считает полный/payload BER |
blocks/block_05_fpga_hdl_flow/rtl/bpsk_zynq_ber_top.v |
обёртка верхнего уровня вокруг источника, цепочек TX/RX и проверки BER |
blocks/block_05_fpga_hdl_flow/tb/tb_bpsk_zynq_ber_top.v |
самопроверяющийся testbench loopback верхнего уровня |
Запуск из корня репозитория:
python blocks/block_05_fpga_hdl_flow/python/generate_bpsk_framed_loopback_vectors.py
python blocks/block_05_fpga_hdl_flow/python/generate_bpsk_rrc_tx_fir_vectors.py
iverilog -g2012 \
-o blocks/block_05_fpga_hdl_flow/tb/tb_bpsk_zynq_ber_top.out \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_symbol_mapper.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_upsampler_8x.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_rrc_tx_fir.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_rrc_rx_fir.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_symbol_timing_sampler.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_hard_decision.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_framed_tx_chain.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_rx_bit_recovery_chain.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_frame_bit_source.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_ber_counter.v \
blocks/block_05_fpga_hdl_flow/rtl/bpsk_zynq_ber_top.v \
blocks/block_05_fpga_hdl_flow/tb/tb_bpsk_zynq_ber_top.v
vvp blocks/block_05_fpga_hdl_flow/tb/tb_bpsk_zynq_ber_top.out
Ожидаемый результат:
PASS: bpsk_zynq_ber_top completed without errors
Контракт верхнего уровня¶
Новый верхний уровень намеренно невелик, но ориентирован на железо:
| Группа сигналов | Роль |
|---|---|
start, busy, done |
однократный запуск кадра и статус завершения |
frame_bit_count, preamble_count, start_offset |
детерминированные управляющие входы, выведенные из общего пакета Блока 11 |
tx_valid, tx_i, tx_q |
поток отсчётов TX к будущему ЦАП, FIFO или тракту TX AD9363 |
rx_valid, rx_i, rx_q |
поток отсчётов RX от будущего АЦП, FIFO или тракта RX AD9363 |
received_bits, total_errors, payload_errors |
компактный интерфейс результата BER |
lock_inverted |
1, если счётчик захватился на инвертированной преамбуле (180 градусов) и перевернул нагрузку обратно; держится после done до следующего start |
Почему этот этап важен¶
Предыдущий testbench loopback доказал, что DSP-цепочка работает. Эта работа добавляет системную структуру, необходимую, прежде чем проект платы сможет её использовать:
- детерминированный источник кадра вместо прямого цикла стимулов в testbench;
- счётчик BER, который позже можно отобразить в память или отдать в ПО;
- чистый шов в домене отсчётов, к которому подключатся AD9363 или AXI/DMA;
- явная семантика завершения верхнего уровня для автоматизированных аппаратных тестов.
Общий контракт кадра¶
Верхний уровень использует:
| Общий артефакт | Роль |
|---|---|
blocks/block_05_fpga_hdl_flow/rtl/bpsk_frame_bits.mem |
детерминированные биты кадра для источника и для проверки BER |
blocks/block_05_fpga_hdl_flow/tb/bpsk_framed_loopback_meta.txt |
начальное смещение, число бит, число символов преамбулы и допущения об очистке |
Так биты источника, эталон BER и метаданные тайминга остаются синхронизированными между отдельным loopback и верхним уровнем, готовым для Zynq.
Стратегия testbench¶
Самопроверяющийся testbench соединяет:
выходы отсчётов TX верхнего уровня -> задержка 24 отсчёта, знак I инвертирован -> входы отсчётов RX верхнего уровня
Инверсия знака намеренная: из-за неё каждый прогон проходит через ветку инвертированного захвата счётчика BER.
Затем проверяет:
startзапускает ровно один кадр;busyстановится активным на время прогона и сбрасывается в конце;doneустанавливается только после завершения всего пакета;- счётчики BER сообщают ноль полных и payload-ошибок;
lock_inverted = 1: счётчик захватился на инвертированной преамбуле, которую даёт инвертированная петля.
Первая интерпретация с точки зрения железа¶
Этот верхний уровень всё ещё синтетический, но теперь структурно совпадает с будущим трактом платы:
источник кадра / управление -> отсчёты TX -> AD9363 TX -> RF-тракт -> AD9363 RX -> отсчёты RX -> счётчики BER
Именно сюда на следующем шаге подключаются регистры AXI-Lite, DMA, управление BRAM или специфичная для платы логика тактов и сброса.
Чего ожидать¶
PASS: bpsk_zynq_ber_top completed without errors (281 bits, payload errors 0)
В отличие от Lab 5.8 и 5.9, верхний уровень содержит bpsk_ber_counter, который находит кадр корреляцией принятых бит с известной преамбулой, а не доверяет фиксированному смещению.
Раннер курса генерирует векторы, компилирует, запускает симуляцию и считает ошибкой любую строку FAIL или ненулевой код выхода симулятора:
python tools/run_block5_hdl_smoke.py --test tb_bpsk_zynq_ber_top
В Vivado 2021.1 этот топ размещается и разводится на xc7z020clg400-2 на 100 МГц с запасом
+1,436 нс и занимает 676 LUT, 3085 триггеров и 192 из 220 блоков DSP48E1 (87 %)
(отчёт): фильтр формирования TX и согласованный фильтр RX берут по 96 (лабораторная
5.6). Всё остальное, чему на том же кристалле нужны умножители, например петля несущей или второй
канал, уже не поместится. Худший путь это разветвление, а не арифметика: покадровый перезапуск RX
(rst || frame_start), идущий на 2560 входов сброса согласованного фильтра, 3 уровня логики и
88 % трассировки.
Упражнения¶
Каждое упражнение ниже: намеренная мутация RTL в одну строку. Внесите её, запустите testbench, прочитайте сообщения и верните файл (git checkout -- <file>). Приведённые выводы получены на Icarus Verilog 12.0.
- Примените ту же инверсию полярности, что в Lab 5.8 и 5.9 (
out_bit <= (in_i >= 0);вbpsk_hard_decision.v). Те два testbench падают с 281 / 256 ошибками. Этот падает иначе:ERROR: lock_inverted=0 on the sign-inverted loopback, expected 1. Найдите причину вbpsk_ber_counter.v: он считает совпадения преамбулы и с прямым шаблоном (m_noninv), и с инвертированным (m_inv), а при инвертированном захвате переворачивает полезную нагрузку. Это разрешение неоднозначности на 180 градусов, и реальному приёмнику оно нужно, потому что петля несущей BPSK может захватиться на 0 или на 180 градусов. Что показали бы одни счётчики ошибок? - Testbench инвертирует петлю намеренно. Зачем, и что PS должен делать с
CONTROL_STATUS[3](Лабораторная 5.11) на железе? - Какова вероятность, что 8 случайных бит точно совпадут с 8-битной преамбулой? Свяжите её с
LOCK_PREAMBLE_BITSиLOCK_ERR_TOL. - Верните поиск кадра, которым счётчик пользовался до issue #65: в
bpsk_ber_counter.vзамените условие((LOCK_ERR_TOL > 0) || (lock_preamble_limit_reg == WIN[INDEX_W-1:0]))на(LOCK_ERR_TOL > 0), чтобы точный захват шёл через последовательное сравнение, и запуститеpython tools/run_block5_hdl_smoke.py --test tb_bpsk_lock_leading_bits. Стенд ставит перед кадром от 0 до 12 лишних бит. Наблюдается:FAIL: tb_bpsk_lock_leading_bits - 27 of 52 runs failed, напримерFAIL 4 zero leading bits, inverted 0: received 281, total errors 140. Кадр принят целиком, и половина его бит неверна. Почему счётчик захватывает кадр не в той позиции, а не отказывается от захвата, и почему лишние нули вредят именно этой преамбуле (00000110...)?
Ответы (сверены с RTL и стендом)
- Мутация компенсирует инверсию петли, поэтому счётчик теперь захватывается на прямой преамбуле, насчитывает ноль полных и payload-ошибок и сбрасывает
lock_invertedв 0. Одни счётчики ошибок показали бы чистый прогон: ошибку полярности они не видят, потому что счётчик в любом случае нормализует нагрузку. Её видно только по флагу. До issue #64 полярность оставалась во внутреннем регистреinvert_bits, и эта мутация проходила незамеченной. - Прямая петля проверяет только неинвертированный захват, и инвертированная ветка (
m_inv,invert_bits, переворот нагрузки) в регрессии никогда бы не выполнялась. Инвертированная петля прогоняет её каждый раз, а проверкаlock_inverted = 1делает ожидаемую полярность частью контракта. На железе читайтеCONTROL_STATUS[3]вместе с каждым результатом BER и записывайте в лог (в gpreg-мосте Блока 11 та же информация лежит вgp_status[6:5]). На линии с фиксированной фазой, например в цифровой петле или петле внутри ПЛИС, значение меняться не должно, и его изменение указывает на ошибку знака в тракте. За петлёй несущей допустимы оба значения, и по логу видно, как часто петля попадает в каждую точку захвата. У верхнего уровня QPSK есть такой же выход: двухбитныйrotation_index(k·90 градусов). - Точное 8-битное совпадение случайных бит имеет вероятность
1/256 ≈ 0,4 %, удвоенную примерно до 0,8 %, потому что принимается любая полярность. СLOCK_ERR_TOL = tокно допускает доtнесовпадений:P = 2 · Σ_{k≤t} C(8,k) / 256, уже 7 % приt = 1. Длинные окна захвата и малый допуск держат ложные захваты редкими; в курсе по умолчанию 8 бит иLOCK_ERR_TOL = 0. - Последовательный поиск сравнивает каждый новый бит со следующим ожидаемым битом преамбулы. При несовпадении он не возвращается назад: считает текущий бит новым первым битом преамбулы и идёт дальше. Это наивный поиск подстроки без функции отказа из алгоритма Кнута-Морриса-Пратта. Преамбула начинается с пяти нулей, поэтому лишние нули перед ней принимаются за её начало. Настоящая
1приходит туда, где поиск ждёт0, а истинное начало уже позади: эти биты поиск уже прошёл и больше на них не смотрит. Он продолжает сравнение с более позднего бита и в конце концов находит позицию, где следующие биты случайно проходят все его проверки. Дальше он считает кадр с неверным выравниванием, и примерно половина бит расходится с ROM (140 из 281). Скользящий коррелятор на каждом новом бите сравнивает всё последнее окно, поэтому истинное начало пропустить не может. На плате та же ошибка проявлялась только при некоторых смещениях старта сэмплера, когда перед кадром появляются лишние биты (отчёт с железа). В обычном порядке работы смещение перебирается и берётся лучшее, поэтому ошибка и оставалась незамеченной.
Чек-лист отчёта¶
- Объяснено значение
start,busyиdone. - Показано, как задаются
frame_bit_count,preamble_countиstart_offset. - Показаны выведенные границы отсчётов TX и RX.
- Приведены итоговые значения счётчиков BER.
- Указан следующий шаг интеграции: регистры AXI-Lite, подключение DMA или соединение с AD9363.
Шаблон инженерного вывода¶
Детерминированная цепочка BPSK-модема теперь имеет верхний уровень, готовый для Zynq, с однократным интерфейсом запуска, явным статусом завершения и счётчиками BER.
Источник кадра и проверка BER используют один и тот же ROM с битовой последовательностью, поэтому тест остаётся детерминированным.
Это правильный якорь интеграции перед подключением проекта к управлению AXI, DMA или тракту отсчётов AD9363.