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

Лабораторная 11.49 — Mailbox разговаривает с модемом

Идея

Лабораторная 5.12 поставила между процессором и ПЛИС маленький mailbox: PS записывает сообщение в регистры, ставит TX_START, ПЛИС копирует его в регистры RX, и PS читает его обратно. Эхо, и это намеренно: вся транзакция PS/PL на виду, и больше ничему сломаться негде. Лабораторная закончилась обещанием, что позже те же регистры понесут сообщение через модем QPSK.

Эта лабораторная обещание выполняет. Программа на плате делает ровно то же, что в Лабораторной 5.12, но за регистрами ПЛИС теперь собирает радиопакет, передаёт его, принимает, проверяет и отдаёт обратно. Ещё один бит в регистре позволяет тому же mailbox ждать сообщение, которое отправила другая плата. Интереснее всего здесь не код, а то, где какая работа оказалась и почему.

Кто что делает

PS (zmbox)          TX_SEQUENCE, TX_LENGTH, TX_DATA, TX_START ... STATUS.RX_VALID, RX_*, RX_ACK
                      |                                               ^
mailbox (такт CPU)    packet-v1: длина, номер, данные, CRC-16         | снять PN9, проверить CRC,
                      отбеливание PN9                                 | заполнить RX_* (CRC_OK / FRAME_ERROR)
                      |  256 бит + тумблер                            |  256 бит + тумблер
мост (такт отсчётов)  голова пачки, преамбула, биты пакета            | скопировать декодированные
                      -> передатчик модема QPSK                       | биты, когда попытка закончилась
                      |                                               |
                      +------ ЦАП -> TX1 ~~~ эфир ~~~ RX1 -> АЦП -----> приёмник (цепочка Лаб. 11.45)
  • Канальный уровень живёт в mailbox. Packet-v1 (формат Лабораторной 11.46 и zmsg), CRC и отбеливание PN9 вычисляются в ПЛИС, в тактовом домене процессора, рядом с регистрами, которым они служат. Модем так и не узнаёт, что его 256 бит это пакет.
  • Физический уровень живёт в модеме. Передатчик выдаёт голову пачки из Лабораторной 11.48 (обучающие символы и опорный), 48-битную преамбулу из ПЗУ кадра, затем 256 бит пакета. Приёмник это цепочка Лабораторной 11.45 без изменений.
  • Мост только запускает попытки и отдаёт биты. Запрос от mailbox запускает попытку так же, как бит старта в gp_ctrl; когда попытка закончилась, мост копирует декодированную полезную нагрузку из своего регистра декодированных бит.
  • Два тактовых домена, между ними тумблеры. Mailbox работает на такте процессора, модем на такте отсчётов. Каждый запрос и каждый ответ это тумблер плюс слово данных, которое стабильно до переключения тумблера и остаётся стабильным после. Импульс бы не подошёл: в Лабораторной 11.48 импульс старта из двух быстрых записей в регистр терялся в двухтриггерном синхронизаторе примерно в каждом седьмом случае.

Регистры

Всё из Лабораторной 5.12 осталось, по базовому адресу 0x79050000:

смещение регистр в этой лабораторной
0x00 ID 0x4D424F58 ("MBOX"), без изменений
0x04 VERSION 0x00020000 (у эха 0x00010000); 0x00020001, когда RX_STOP ещё и заканчивает идущую попытку прослушивания (issue #71)
0x08 CONTROL бит 0 TX_START, бит 1 RX_ACK как раньше; бит 2 RX_LISTEN, бит 3 RX_STOP
0x0C STATUS биты 0-3 как раньше (TX_BUSY, TX_DONE, RX_VALID, RX_OVERFLOW); бит 4 LISTENING
0x10, 0x14 TX_SEQUENCE, TX_LENGTH не больше 27 байт; более длинное сообщение не передаётся и завершается с FRAME_ERROR
0x20-0x5C TX_DATA байты нагрузки, в словах little-endian
0x60-0x68 RX_SEQUENCE, RX_LENGTH, RX_META RX_META: бит 0 CRC_OK, бит 1 FRAME_ERROR (кадра нет или длина вне допустимой)
0x70-... RX_DATA принятая нагрузка, нули за пределами RX_LENGTH

TX_START передаёт сообщение и в той же попытке принимает то, что вернётся: на одной плате с кабелем TX1 -> RX1 это её собственная пачка, и mailbox работает как эхо из Лабораторной 5.12, только через радио. RX_LISTEN заставляет mailbox ждать сообщения извне: мост одну за другой запускает попытки в режиме прослушивания (Лабораторная 11.48), кадры с совпавшей CRC попадают в регистры RX, всё остальное отбрасывается. Пока mailbox слушает, TX_START не принимается.

Mailbox не отдельный блок в блок-дизайне. Он сидит внутри обёртки моста и добавляет на шину процессора один слейв AXI-Lite, поэтому 256-битные шины не покидают один модуль. В ядро обёртка передаёт только младшие 8 бит адреса, потому что шина передаёт полный адрес: та самая ловушка Лабораторной 5.12.

Без оборудования

python tools/run_block5_hdl_smoke.py --test tb_message_modem_loopback --test tb_message_modem_listen

tb_message_modem_loopback работает с обёрткой по адресу платы, ЦАП замкнут на вход приёмника: отправляет 18-байтное сообщение и ждёт его обратно с CRC_OK, а также проверяет, что 30-байтное сразу завершается с FRAME_ERROR. tb_message_modem_listen соединяет две копии обёртки: T отправляет два сообщения, R слушает и должен доставить оба. На АЦП R приходит пачка T, пока T передаёт, а в остальное время немного шума, потому что детектор пакета в режиме прослушивания учится уровню шума на тишине. (Первая версия этого тестбенча собирала шум на один бит уже, чем нужно, и «шум» оказывался почти на полной шкале; приёмник поймал первое сообщение случайно и потерял второе. Исправление было в тестбенче, а не в схеме.)

Две платы: сообщение с платы A попадает в mailbox платы B

Стенд как в Лабораторной 11.48: плата A TX1 -> 30 дБ -> сплиттер -> плата B RX1, 915 МГц. На плате B нужен образ issue55-mailbox-listen из репозитория артефактов (sha256 530e0571...). Плата B работает с RAM-диска, поэтому zmsg и zmbox нужно заново копировать в /root после каждой перезагрузки, включая ту, что загружает новый образ.

board-b$ ./zmbox listen --count 3 --timeout 30
board-a$ ./zmsg stop
transmitter stopped
board-a$ ./zmsg send "into the mailbox 0" --seq 70 --once --tx-gain -30
TX once sequence=70 bytes=18 payload="into the mailbox 0" lead=64
board-a$ ./zmsg send "into the mailbox 1" --seq 71 --once --tx-gain -30
TX once sequence=71 bytes=18 payload="into the mailbox 1" lead=64
board-a$ ./zmsg send "into the mailbox 2" --seq 72 --once --tx-gain -30
TX once sequence=72 bytes=18 payload="into the mailbox 2" lead=64
board-b:  RX sequence=70 bytes=18 meta=CRC_OK payload="into the mailbox 0"
board-b:  RX sequence=71 bytes=18 meta=CRC_OK payload="into the mailbox 1"
board-b:  RX sequence=72 bytes=18 meta=CRC_OK payload="into the mailbox 2"
board-b:  listened 5.9 s, delivered 3, overflows seen 0

zmbox listen записывает RX_LISTEN, а дальше только опрашивает STATUS.RX_VALID, читает RX_SEQUENCE, RX_LENGTH, RX_META и RX_DATA и пишет RX_ACK. Ни одного бита радиокадра он не видит: снятие PN9 и проверка CRC прошли в ПЛИС. zmsg receive --listen из Лабораторной 11.48 делает ту же работу на PS: читает декодированные биты и проверяет CRC программно.

cd blocks/block_11_integrated_sdr_project/python
python two_board_zmsg_once.py --messages 500 --receiver zmbox --json-out mailbox_listen.json

На стенде курса, плата B на 71 дБ:

TX приёмник отправлено доставлено принято чужих
-30 дБ mailbox (zmbox listen) 500 498 0
-30 дБ программа на PS (zmsg receive --listen) 500 498 0
-50 дБ mailbox (zmbox listen) 200 168 0

Перенос канального уровня в ПЛИС ничего не стоит: те же 498 из 500 в обоих случаях.

Одна плата: mailbox отправляет и получает своё сообщение обратно

Стенд как в Лабораторной 11.47: плата B TX1 -> аттенюаторы (не меньше 30 дБ; на стенде курса переменный аттенюатор на 0 дБ, 30 дБ и сплиттер) -> плата B RX1. zmbox send готовит радиотракт так же, как Лабораторная 11.47 (каналы ЦАП на вход DMA, циклический буфер нулей для FIFO ЦАП, гетеродины и усиления), а дальше работает только с регистрами mailbox.

board-b$ ./zmbox send 'Hello from the mailbox' --seq 17 --tx-gain -20
TX sequence=17 bytes=22 payload="Hello from the mailbox"
RX sequence=17 bytes=22 meta=CRC_OK payload="Hello from the mailbox"
board-b$ ./zmbox send 'this message is too long for packet v1' --seq 18 --tx-gain -20
payload is 38 bytes; the mailbox modem sends at most 27 (expect FRAME_ERROR)
TX sequence=18 bytes=38 payload="this message is too long for packet v1"
RX sequence=0 bytes=0 meta=FRAME_ERROR payload=""
board-b$ ./zmbox stop
transmitter stopped

--count N повторяет сообщение N раз с растущими номерами и печатает, сколько вернулось. На стенде курса, плата B на 60 дБ, по 200 сообщений на точку:

усиление TX 0 -10 -20 -30 -35 -38 -40 -42 -45 -50 -60
CRC_OK из 200 163 200 199 199 200 198 80 19 13 125 1

При TX -20 дБ длинный прогон доставил 5000 из 5000. Ни разу не было принято сообщение с неверной нагрузкой.

В таблице есть две вещи, на которые стоит посмотреть. На 0 дБ приёмник, скорее всего, перегружен (60 дБ усиления за примерно 35 дБ затухания тракта). А между -40 и -50 дБ кривая немонотонна: проваливается на -42...-45 дБ и частично восстанавливается на -50. Аттенюатор TX точен (это измерено в задаче #29), так что дело не в нём. Ещё до подключения кабеля плата B принимала собственную пачку через какую-то утечку на плате, причём уровень почти не зависел от усиления TX. С кабелем путей становится два, с разной задержкой, и около -42 дБ они примерно равны по силе: это вело бы себя как двухлучевое замирание. Объяснение согласуется с цифрами, но не проверялось.

Упражнения

  1. Лабораторная 5.12 обещала, что та же программа будет работать с модемом за регистрами. Какими регистрами пользуется zmbox send и что в них поменял модем? Что дал регистр версии?
  2. Почему запрос и ответ между mailbox и мостом сделаны тумблерами со стабильными данными, а не импульсами? Что именно пошло бы не так с импульсом длиной в один такт из домена процессора?
  3. Во время прослушивания mailbox отбрасывает кадры с несовпавшей CRC и ничего не сообщает PS. Приведите один довод оставить так и один довод считать их в регистре.
  4. RX_VALID остаётся взведённым до RX_ACK. Что будет, если до подтверждения первого сообщения придут два, и как PS об этом узнает?
Ответы
  1. TX_SEQUENCE, TX_LENGTH, TX_DATA, CONTROL (TX_START, RX_ACK), STATUS, RX_SEQUENCE, RX_LENGTH, RX_META и RX_DATA, ровно набор Лабораторной 5.12. Поменялся их смысл во времени и в деталях: TX_LENGTH теперь ограничен 27 байтами (нагрузка packet-v1), RX_META может сказать FRAME_ERROR, потому что радиокадр бывает потерян, а ответ приходит через миллисекунды, а не через такт. VERSION (0x00020000 против 0x00010000) позволяет программе узнать, с каким mailbox она работает, прежде чем полагаться на всё это.
  2. Такт процессора и такт отсчётов не связаны. Импульс длиной в такт может попасть между двумя фронтами другого такта и остаться незамеченным; в Лабораторной 11.48 именно так терялся примерно каждый седьмой старт. Тумблер держит новый уровень до следующего запроса, поэтому синхронизатор всегда увидит изменение, а данные рядом стабильны задолго до и после, поэтому их можно взять без собственного синхронизатора.
  3. За простоту: почти все такие кадры это ложные захваты на шуме (около 0,1 в секунду прослушивания, Лабораторная 11.48), так что отчёт о них в основном сообщал бы о шуме. За счётчик: если он растёт, а ожидаемые сообщения не приходят, пользователь видит, что связь на грани, а не что эфир молчит.
  4. Mailbox сохраняет первое сообщение и взводит RX_OVERFLOW (бит 3 в STATUS); второе теряется. PS видит бит переполнения, когда читает STATUS, а RX_ACK сбрасывает оба. Приёмнику, который должен поспевать за пачками, нужна очередь, а у этого mailbox её намеренно нет.

Доказательства

Что лабораторная показывает и чего нет

Она показывает, как регистровый контракт Лабораторной 5.12 несёт настоящие радиосообщения: от другой платы прямо в mailbox, с проверкой пакета в ПЛИС, так же надёжно, как программный приёмник. Она не показывает очередь (одно сообщение за раз) и использует приёмник Лабораторной 11.45 со всеми его ограничениями (Лабораторная 11.48: прослушиванию нужны паузы между пакетами). Все числа получены на одной паре плат, на одной несущей, по кабелю.

Лабораторная 11.50 запускает mailbox в обе стороны сразу, как чат между двумя платами, и находит три неисправности, которые односторонняя схема этой лабораторной прятала.