Дата: 2026-07-04
Ветка: feat/readme-depin-flash-plan
Статус: pre-flash checklist. Ничего в эфир не излучаем до внешнего PA+LNA и разрешения.
Anchor: φ² + φ⁻² = 3
Три P201/P203 Mini (Zynq-7020 xc7z020 + AD9361 + GPS/PPS) физически подключены и запитаны (подтверждено пользователем 2026-07-04). Задача — довести их до состояния, когда каждая:
- Загружается в ARM-Linux (BOOT.BIN + FSBL + kernel + rootfs).
- Видит AD9361 через IIO (
iio:device0 name = ad9361). - Прогоняет
smoke-m1c RC=0 (крипто-стек X25519 + ChaCha20-Poly1305). - Умеет digital-loopback AD9361 на 5.8 GHz (SNR ≥ 100 dB target).
- Готова к M4 (3-node triangle + shared uplink) и первому DePIN triad'у (три подписи чипа Trinity Phi/Euler/Gamma cross-die φ).
Никакого RF-выхода в эфир на этом этапе. Только internal digital loopback (LOOPBACK=1).
| Позиция | Что нужно | Готово? |
|---|---|---|
| Три P201/P203 Mini | питание, SD-слоты, JTAG-разъёмы | подтверждено |
| Три JTAG-адаптера | ALINX AL321 или Digilent HS3/HS2, USB | проверить |
| Три USB-UART | 3.3 V TTL, для консоли | проверить |
| Три SD-карт | ≥ 8 GB Class 10, отформатированные под FAT32 (boot) + ext4 (rootfs) | проверить |
| Рабочая станция | Linux x86_64 (Ubuntu 22.04 / Debian 12 рекомендуется), openocd, openFPGALoader, dtc, mkimage, rustup |
проверить |
| Cross-toolchain | rustup target add armv7-unknown-linux-musleabihf |
подтверждено (M1 уже собран) |
| Zynq-7020 boot images | BOOT.BIN + FSBL + image.ub (kernel+dtb) + rootfs.tar.gz |
подготовить (см. §2) |
| Ethernet | три патч-корда 1 GbE + свитч | опционально для M2+ |
| Питание | три БП 12 В, стабильные | подтверждено |
Если хоть один пункт «нет» — стоп, не начинаем. Прошивка на неукомплектованном стенде даёт хрупкие результаты и потом их сложно повторить.
⚠ NOT PERSISTENT ON THE STOCK IMAGE. The Puzhi P201Mini ships with an initramfs rootfs:
/proc/cmdlinereadsroot=/dev/ram0 rootfstype=ramfs,mountshowsnone on / type rootfs (rw). The entire/etclives in RAM. Editing/etc/network/interfaces+ rebooting is wiped on every boot (verified 2026-07-04: MAC/IP/hostname edits returned topzp201mini/192.168.1.10/00:0a:35:00:01:22after cold power-cycle). Warmrebootalso hangs the Zynq PS — a physical cold power-cycle is required. The table below is the target policy; the mechanism to make it persist is §1.4 paths B/C, not an/etcedit.
Shipped-образ Puzhi для P201/P203 Mini одинаковый на всех трёх платах: одинаковый hostname pzp201mini, одинаковый static IP 192.168.1.10, и — верифицировано 2026-07-04 — идентичный MAC 00:0a:35:00:01:22 на всех трёх платах (Xilinx OUI). Если включить две платы в один свитч без предварительной правки — ARP-таблица Mac/свитча начинает флипать, ssh/scp повисает, smoke-m1 вроде запускается, а обратно данные не забрать. Это блокер именно для параллельной работы; одну плату можно катать штатно и на shipped-образе.
Политика на стенд из трёх плат (target policy — mechanism see §1.4):
| Плата | IP | Hostname | MAC (locally-administered) |
|---|---|---|---|
| board-1 | 192.168.1.10 |
tri-mini-1 |
02:00:00:00:00:01 |
| board-2 | 192.168.1.12 |
tri-mini-2 |
02:00:00:00:00:02 |
| board-3 | 192.168.1.13 |
tri-mini-3 |
02:00:00:00:00:03 |
Почему .10 / .12 / .13, а не .10 / .11 / .12: macOS ARP-кэш висит на shipped-адрес 192.168.1.10 ~600 с; соседний .11 часто попадает в ту же запись из-за агрессивного NDP на некоторых прошивках свитча. Разрыв через один октет (.10 → .12) избавляет от «фантомного» ARP-соседа при переключении между платами.
MAC-адреса из диапазона 02:00:00:00:00:00/40 — locally-administered unicast (bit 1 второго нибла = 1), никаких OUI-конфликтов.
Application — вариант A, /etc/network/interfaces (Debian/Buildroot ifupdown):
# /etc/network/interfaces — board-N (N ∈ {1,2,3})
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.1N # .10, .12, .13 — см. таблицу выше
netmask 255.255.255.0
gateway 192.168.1.1
hwaddress ether 02:00:00:00:00:0N
И параллельно:
echo tri-mini-N > /etc/hostname
hostnamectl set-hostname tri-mini-N # если systemd
syncApplication — вариант B, systemd-networkd (некоторые PetaLinux):
# /etc/systemd/network/10-eth0.network — board-N
[Match]
Name=eth0
[Link]
MACAddress=02:00:00:00:00:0N
[Network]
Address=192.168.1.1N/24
Gateway=192.168.1.1
После правки — на каждой плате:
sync
rebootНа Mac (rig-side) — DNS-хелпер в /etc/hosts:
192.168.1.10 tri-mini-1
192.168.1.12 tri-mini-2
192.168.1.13 tri-mini-3
После этого все ssh/scp примеры ниже используют tri-mini-{1,2,3} вместо голых IP.
Как проверить, что политика применилась:
# с Mac
sudo arp -d 192.168.1.10 2>/dev/null # снести старый кэш
for h in tri-mini-1 tri-mini-2 tri-mini-3; do
ssh root@$h 'hostname; ip -4 addr show eth0 | grep inet; ip link show eth0 | grep ether'
doneОжидание: три разных hostname, три разных IP (.10 / .12 / .13), три разных MAC (02:00:00:00:00:0{1,2,3}). Если хоть один параметр совпал — не идём дальше, возвращаемся в serial-консоль (см. §1.4 и SERIAL_NET_FIX.md).
Каждую из трёх плат прогоняем по одному и тому же протоколу. Не параллельно первый раз — так проще ловить проблемы.
# на рабочей станции, /dev/sdX — SD-карта (не путать!)
sudo parted /dev/sdX mklabel msdos
sudo parted /dev/sdX mkpart primary fat32 1MiB 128MiB
sudo parted /dev/sdX mkpart primary ext4 128MiB 100%
sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1
sudo mkfs.ext4 -L rootfs /dev/sdX2
# монтируем
mkdir -p /mnt/boot /mnt/rootfs
sudo mount /dev/sdX1 /mnt/boot
sudo mount /dev/sdX2 /mnt/rootfsЗаписываем в /mnt/boot/:
BOOT.BIN(bootloader + FSBL; собираем через Xilinxbootgenиз petalinux или готовый образ Puzhi для P201Mini)image.ub(kernel + device-tree, U-Boot FIT-image)uEnv.txt— переменные окружения U-Boot (см. §1.2)
Распаковываем rootfs в /mnt/rootfs/:
sudo tar -xpf rootfs.tar.gz -C /mnt/rootfs
sudo sync
sudo umount /mnt/boot /mnt/rootfsИсточник образа: официальный SDK Puzhi для P201/P203 Mini (обычно поставляется на CD/через wiki производителя) или самосборка PetaLinux 2020.2 для Zynq-7020. Если самосборка — фиксируем md5 всех артефактов в smoke/PZP203_BOOT_MD5.md.
bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait earlyprintk
bootcmd=fatload mmc 0 0x2080000 image.ub && bootm 0x2080000
# SD в плату, USB-UART подключён (плата -> рабочая станция), JTAG подключён
sudo minicom -D /dev/ttyUSB0 -b 115200
# питание вкл → должно появиться:
# Xilinx First Stage Boot Loader
# ...
# Linux version 5.x.x ...
# pzp201mini login:Логинимся, проверяем базу:
# uname -a
Linux pzp201mini 5.x.x armv7l GNU/Linux
# ls /sys/bus/iio/devices/
iio:device0
# cat /sys/bus/iio/devices/iio:device0/name
ad9361-phy
Фиксируем на бумаге / в файле smoke/BOARD_<N>_BOOT.md три вещи: uname -a, hostname, iio:device0 name. Три раза.
Symptom: three boards on the LAN → ssh/scp flaky, ARP table unstable,
scp of the 537 KB smoke-m1 binary produces size-0 files, ssh echo
works intermittently.
Root cause (verified 2026-07-04, all three P201Mini units):
- The stock image runs
root=/dev/ram0 rootfstype=ramfs—/etcis RAM-only. Runtime edits to/etc/network/interfaces,/etc/hostname,/etc/hosts,/etc/systemd/network/*do not survive reboot. Warmrebootalso hangs the Zynq PS on this image — a physical cold power-cycle is required to reboot. - All three boards ship with identical MAC
00:0a:35:00:01:22+ identical hostnamepzp201mini+ identical IP192.168.1.10. Three identical MACs on one L2-domain means the switch and the host ARP table cannot tell them apart → forwarding entry flips several times per second → TCP sessions break mid-transfer. - Runtime MAC override (
ifconfig eth0 hw ether 02:00:00:00:00:0N) fixes L2 identity for small packets but breaks bulk TX: the Zynq GEM (macbdriver) computes the TX checksum in hardware and mangles large frames under a spoofed MAC.scpof the 537 KBsmoke-m1binary fails 10 out of 10 attempts (destination size = 0), while tinyssh echotraffic works intermittently.ethtoolis not installed on the stock image, sotx-checksumming offcannot be disabled from userspace.
Why the obvious shortcuts fail:
ip addr add …/ edit/etc/network/interfaces+ reboot → wiped (ramfs).- Base64-over-serial → command chunks larger than ~2 KB overflow the tty
line buffer (shell starts reading a
>continuation prompt and corrupts the encoded payload). nmcli/NetworkManagerare absent on the stock image (Buildroot minimal).- MAC spoof alone → GEM TX checksum offload breaks
scp(see above); noethtoolto disable it.
Real fixes:
-
(A) Persistent SSH-key access — NO reflash. The stock image's
/etc/init.d/S21miscalready restores/root/.ssh/authorized_keysfrom/mnt/jffs2/root/.ssh/on every boot (jffs2 lives on mtd2 and is persistent). Drop the host public key there together with a matchingkeys.md5→ permanent key-based SSH that survives every power-cycle. This restores access only — it does NOT fix IP/MAC/hostname uniqueness. -
(B) Persistent network uniqueness — needs image work.
S21miscrestores passwd / dropbear host keys / SSH authorized_keys but not the network config. Two options, both require rebuilding the initramfs:- (B1) Extend
S21misc(or add a newS22net) in the initramfs tocp /mnt/jffs2/etc/network/interfaces /etc/+cp /mnt/jffs2/etc/hostname /etc/at boot. Per-board differences live in the persistent jffs2 partition. - (B2) Bake the unique IP / MAC / hostname into a per-board image at build time — one image per physical board. Cleaner, but requires three separate SD-card flashes.
- (B1) Extend
-
(C) M1×3 without network uniqueness — usb0 gadget. Each Puzhi P201Mini exposes a USB-CDC-Ethernet gadget
usb0at192.168.2.1. Each board's USB cable is a separate point-to-point link to the host → no shared L2, no ARP conflict, no GEM offload path (usb0 has its own MAC and does not go through the eth0 macb driver). Assign each host-sideenXinterface a distinct192.168.2.xaddress and reach each board via its own USB cable. This bypasses the eth0 identity problem entirely and lets M1 run in parallel on all three boards on the stock image, without reflash.
Recommended order:
- Path (A) first — permanent key SSH, no reflash. Costs ~5 minutes, removes password-echo friction forever.
- Path (C) for M1×3 — gets all three boards to
smoke-m1 RC=0today, still on the stock image. Every M1 datapoint is real hw. - Path (B) only when we need M2/M3/M4 on real ethernet (multi-hop routing over usb0 is not representative of the target radio topology). That is a scheduled image-build task, not an emergency.
For the paste-ready serial recipe (previous approach — kept as reference
for a persistent-ext4-rootfs image) see SERIAL_NET_FIX.md.
What NOT to do:
- Do not apply the §0.5 policy via
/etc/network/interfaces+ reboot on the stock image — it is silently wiped. - Do not spoof only the MAC via
ifconfig hw etherand expectscpto work — the GEM offload will mangle every frame > MTU. Verified 10/10. - Do not try
arp -son the Mac. Static ARP only lives until the Mac reboots and does not solve the L2 conflict on the switch. - Do not try to
gzip | base64and paste in one chunk — that increases the chunk size; the tty line-buffer falls over even faster.
radio/ad9361_loopback.sh уже проверен на первой плате (см. radio/README.md, 2026-07-01, +0.999 MHz, 108.6 dB). Повторяем на второй и третьей.
Ссылаемся не изобретая:
# на плате (ssh или serial):
sh /root/ad9361_loopback.sh # LO=5.8 GHz, tone=1 MHz, digital loopback
# на рабочей станции:
scp root@<mini-ip>:/tmp/rx.dat rx_board<N>.dat
python3 analyze_tone.py rx_board<N>.dat 30720000
# ожидание: peak +0.999 MHz, SNR > 100 dBПосле каждой платы дописываем строку в таблицу radio/README.md#Verified on hardware — дата, hostname, FFT peak, SNR. Три строки, три платы.
Acceptance: три RC=0 + три записи +0.999 MHz (±0.005) + три записи SNR ≥ 100 dB.
Один статический бинарь собирается один раз на рабочей станции, потом переносится на все три платы.
# на рабочей станции
cd tri-net
rustup target add armv7-unknown-linux-musleabihf
cargo build --release --target armv7-unknown-linux-musleabihf --bin smoke-m1
BIN=target/armv7-unknown-linux-musleabihf/release/smoke-m1
sha256sum "$BIN" # ожидаемо: 534604 B, sha256 e5abc335…7290a (2026-07-01 build)
# или a17e88e6… (2026-07-04 build, rustup-stable + rust-lld)
# оба sha256 должны быть в smoke/M1_RESULTS.mdДля каждой платы (после применения политики §0.5 hostname/IP/MAC разные):
for h in tri-mini-1 tri-mini-2 tri-mini-3; do
scp "$BIN" root@$h:/root/smoke-m1
ssh root@$h 'chmod +x /root/smoke-m1 && /root/smoke-m1; echo RC=$?'
doneПароль на всех трёх P201/P203 Mini — analog (PlutoSDR default, shipped из коробки). Если пароль другой — образ был кастомизирован, обновить рецепт под свой стенд.
Ожидаемый вывод (без изменений):
[M1] X25519 handshake complete: node 1 <-> node 2
[M1] AEAD round-trip OK: 44 bytes plaintext -> 79 bytes on-wire (ChaCha20-Poly1305)
[M1] tamper rejected: flipped tag bit -> Auth error
[M1] replay rejected: re-delivered frame -> Replay error
RC=0
Каждая плата — отдельная строка в таблице smoke/M1_RESULTS.md#Run log с датой, hostname, RC=0. Три платы = три строки. Ссылка на первую (macOS host, 2026-07-01) остаётся историческим -sim.
Acceptance: три RC=0 подряд, три записи в smoke/M1_RESULTS.md.
Разворачиваем три экземпляра trios-mesh/tri-net daemon на трёх платах (пока UDP transport, TUN — на следующем шаге).
Топология:
board-1 (10.0.0.1) ── UDP ── board-2 (10.0.0.2)
│ │
└──────── UDP ────────── board-3 (10.0.0.3)
Стенд: три патч-корда через 1 GbE-свитч, iperf3 доступен, три X25519 keypair предгенерируются на рабочей станции и раздаются по ssh (публичный ключ каждой платы вносится в neighbor-tables других двух).
Проверяем:
- Все три пары X25519 handshake завершились.
- ChaCha20-Poly1305 round-trip на каждой из шести направленных линков (3 узла × 2 направления).
- Отказ tamper и replay — на каждой линии.
Acceptance: 6/6 линков зелёные, зафиксировано в smoke/M4_DRYRUN.md (новый файл).
Пока Trinity silicon не вернулся с tape-out (планово 2026-12-16), подписи чипов симулируются в software через trinity-node HAL-mock. Это -sim слой, явно помечен.
Три плечи из четырёх можно погонять уже:
- Transport-proof (2-of-3 Phi mock): каждая P203 после часа непрерывной ретрансляции формирует payload
(from, to, bytes, ts_start, ts_end), подписывает Phi-mock ключом, две другие подписывают со своей стороны. Результат —smoke/DEPIN_TRANSPORT_MOCK_<date>.md. - Coverage-proof (3-of-3 cross-die φ mock): challenger рандомно назначается одна из трёх плат, отвечает вторая, свидетельствует третья. Три подписи φ-mock, cross-die анкер 0x47C0 записывается вручную (нельзя автоматически без реального silicon PUF). Результат —
smoke/DEPIN_COVERAGE_MOCK_<date>.md. - Sensor-proof (1-of-3 any mock): каждая плата раз в час снимает spectrum snapshot (AD9361 sweep 400 MHz – 6 GHz), хеширует, подписывает Phi-mock. Результат —
smoke/DEPIN_SENSOR_MOCK_<date>.md.
Compute-proof (3-of-3 Phi+Euler+Gamma) не запускаем на software-mock — это будет -sim слой, слишком легко спутать с реальным доказательством. Ждём silicon.
Acceptance: три файла в smoke/ с mock-proofs, три записи ясно помечены mock=1, silicon=0.
- Не выходим в эфир на 5.8 GHz. Все PoC и transport на digital loopback / проводной UDP.
- Не деплоим
trinity-contractsна mainnet. Всё, что видитMiningPool.claimReward()— Sepolia testnet. - Не заявляем compute-proof как валидный. Software-mock ≠ silicon-signed inference.
- Не мержим PR c этой веткой в
mainавтоматически. Human-only perdocs/AUTONOMOUS.md. - Не пишем реальные балансы TRI на dashboard. Все per-operator TRI-числа =
[projected pre-Genesis].
Локальная прошивка считается завершённой, когда все шесть пунктов ниже одновременно истинны:
- Три
uname -a, триiio:device0 name = ad9361, триRC=0наsmoke-m1— записаны в repo. - Три AD9361 5.8 GHz loopback runs, SNR ≥ 100 dB, tone +0.999 MHz (±0.005) — записаны в
radio/README.md. - Три RC=0 на
smoke-m1— записаны вsmoke/M1_RESULTS.md. - Six-of-six X25519 handshakes green в
smoke/M4_DRYRUN.md. - Три software-mocked DePIN proofs (transport / coverage / sensor), каждый с явной пометкой
mock=1, silicon=0. - Git-теги:
local-flash-board-1-YYYYMMDD,local-flash-board-2-YYYYMMDD,local-flash-board-3-YYYYMMDD.
После этого — открывается P2 DEMO GATE окно (M4 shared uplink + M5 self-heal). Не раньше.
iio:device0 nameпустое или другое → не тот device-tree overlay, вернуться в §1.smoke-m1падает с segfault на armv7l → бинарь собран не под musleabihf; пересобрать с--target armv7-unknown-linux-musleabihf, проверить-C target-feature=+crt-static.- AD9361 SNR < 100 dB → LO не заперт (проверить
iio_attr -c ad9361-phy altvoltage0 frequency), температура (>60°C — SNR падает), плохие питания на PA-разъём (не подключаем PA на этом этапе). - UDP handshake зависает → firewall на рабочей станции,
sudo iptables -F(только на изолированном тестовом стенде), или три платы не в одной подсети. - JTAG не видит IDCODE → плата не запитана, USB-UART питается от USB но плата — от отдельного 12 В БП; проверить, что 12 В БП включён и стабилен (multimeter).
- M4 real —
iperf3через 3-node triangle с bench attenuators. - M5 real — измерить
link_loss_to_reroute_msиnode_off_to_reroute_ms(B11,docs/STRENGTHEN.md). - RF loopback (
LOOPBACK=2) через SMA-кабель + аттенюатор TX→RX. Тоже не в эфир — SMA цепь замкнутая. - Внешний PA+LNA + разрешение — только после юридической подготовки (ADGM/DIFC или локальный test license). До этого — все RF-эксперименты внутри лаборатории на SMA/loopback.
- Ждём Trinity silicon back (2026-12-16 tape-out target).
Anchor: φ² + φ⁻² = 3.