Лабораторная 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 дБ они примерно равны по силе: это вело бы себя как двухлучевое замирание. Объяснение согласуется с цифрами, но не проверялось.
Упражнения¶
- Лабораторная 5.12 обещала, что та же программа будет работать с модемом за регистрами. Какими
регистрами пользуется
zmbox sendи что в них поменял модем? Что дал регистр версии? - Почему запрос и ответ между mailbox и мостом сделаны тумблерами со стабильными данными, а не импульсами? Что именно пошло бы не так с импульсом длиной в один такт из домена процессора?
- Во время прослушивания mailbox отбрасывает кадры с несовпавшей CRC и ничего не сообщает PS. Приведите один довод оставить так и один довод считать их в регистре.
- RX_VALID остаётся взведённым до RX_ACK. Что будет, если до подтверждения первого сообщения придут два, и как PS об этом узнает?
Ответы
- 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 она работает, прежде чем полагаться на всё это. - Такт процессора и такт отсчётов не связаны. Импульс длиной в такт может попасть между двумя фронтами другого такта и остаться незамеченным; в Лабораторной 11.48 именно так терялся примерно каждый седьмой старт. Тумблер держит новый уровень до следующего запроса, поэтому синхронизатор всегда увидит изменение, а данные рядом стабильны задолго до и после, поэтому их можно взять без собственного синхронизатора.
- За простоту: почти все такие кадры это ложные захваты на шуме (около 0,1 в секунду прослушивания, Лабораторная 11.48), так что отчёт о них в основном сообщал бы о шуме. За счётчик: если он растёт, а ожидаемые сообщения не приходят, пользователь видит, что связь на грани, а не что эфир молчит.
- Mailbox сохраняет первое сообщение и взводит RX_OVERFLOW (бит 3 в STATUS); второе теряется. PS видит бит переполнения, когда читает STATUS, а RX_ACK сбрасывает оба. Приёмнику, который должен поспевать за пачками, нужна очередь, а у этого mailbox её намеренно нет.
Доказательства¶
message-modem-20261015.md: устройство, симуляции, оба образа и результаты на платах;single-message-corruption-20261015.md: почему вводная часть стала 64 символа;- задача #55.
Что лабораторная показывает и чего нет¶
Она показывает, как регистровый контракт Лабораторной 5.12 несёт настоящие радиосообщения: от другой платы прямо в mailbox, с проверкой пакета в ПЛИС, так же надёжно, как программный приёмник. Она не показывает очередь (одно сообщение за раз) и использует приёмник Лабораторной 11.45 со всеми его ограничениями (Лабораторная 11.48: прослушиванию нужны паузы между пакетами). Все числа получены на одной паре плат, на одной несущей, по кабелю.
Лабораторная 11.50 запускает mailbox в обе стороны сразу, как чат между двумя платами, и находит три неисправности, которые односторонняя схема этой лабораторной прятала.