T27.AI

Блог

Квитанции с платы никто не проверял. Теперь проверены все 403 200.

2026-09-27 · 11 мин чтения

[измерено на FPGA: все 42 обученные матрицы, проверена каждая квитанция; активации — тестовые векторы, а не прямой проход] AX7203 посчитала 28 416 из 28 416 строк матриц весов обученной модели tern_tc бит-точно, а её харнесс сообщил о 284 160 аутентифицированных квитанциях, не сравнив ни одного тега. Исправленный харнесс падает на семи видах неисправной ячейки. На плате он затем проверил все 403 200 квитанций по всем 42 тернарным матрицам, 33 792 из 33 792 строк бит-точно, и ещё 51 840 для w_down с int8-активациями. Первые длинные прогоны остановились, когда канал UART терял байты при 64 задачах в полёте. Проверка, зарегистрированная до запуска, поместила порог потерь на приёмный буфер USB-моста в 512 байт, а при 24 задачах в полёте не потерялось ни одного байта.

#IGLA#FPGA#Ternary#Verification#TRINET#Correction

Модель IGLA размером с плату, tern_tc, — это 9,08M параметров. Из них 6 451 200 — тернарные веса блоков: 42 матрицы в шести слоях. Модель обучена на 2,0 млрд токенов кода до 0,7613 бит на байт на валидации. Плата — ALINX AX7203 (XC7A200T) с ячейкой узла TRI-NET, собранной открытым тулчейном openXC7. Ячейка считает тернарное скалярное произведение на 32 трита без умножителя и без DSP48. На каждую задачу она отвечает результатом, nonce задачи, id узла и тегом SipHash-2-4 по всему этому. Хост режет каждую строку шириной 320 на десять задач по 32 трита и складывает ответы.

Что сделала плата

Прогон (AX7203, 1 144 744 бод)ЗадачСтрок бит-точноТеги квитанций сверялись?Время
Случайный тернарный matvec 320x3203 200320 / 320да0,67 с
Слой 0, wq, обученные веса12 8001 280 / 1 280нет2,7 с
wq, wo, gate, up во всех шести слоях284 16028 416 / 28 416нет62,4 с

Активации были тернарными векторами, а не int8-активациями модели. Это показывает, что обученные матрицы весов считаются точно. Прямым проходом модели это не является.

Квитанции в двух последних строках никто не сверял

Харнесс слоя собирал прообраз каждой квитанции и выбрасывал его, а загруженный ключ не использовался вовсе. Аутентификацией он считал байт статуса 0x01. Возврат nonce он не проверял и сравнивал только суммы строк, так что две ошибки в кусках могли взаимно погаситься. Мы подключили к нему программную модель ячейки, которая подписывает каждый ответ ключом, которого у хоста нет. Он напечатал «receipts authenticated 160/160» и PASS.

Что остаётся в силе: каждое значение строк в таблице совпало с CPU-оракулом, а прогон случайной матрицы свои теги сверял. Что отозвано: «284 160 квитанций аутентифицировано под ключом node0», потому что эти квитанции никто не проверял. С тех пор исправленный харнесс измерил на плате все 42 матрицы, включая эти 24, с проверкой каждой квитанции (ниже).

Исправленный харнесс и семь способов его завалить

Теперь ответ засчитывается, только если выполнены все пять условий. Статус равен 0x01. Nonce выдан этим прогоном, и ответ на него пришёл один раз. Id узла совпадает с первым ответом. Тег SipHash пересчитывается под ключом. y равен скалярному произведению этого куска. Строка проходит, только если прошли все её куски и их сумма равна скалярному произведению строки, посчитанному прямо по int8-весам модели, а не по упакованным байтам провода. Для каждой проверки показано, что она умеет падать:

Что ячейка делает не такВердикт харнесса
подписывает чужим ключомотвергнуто: tag
отдаёт неверный ответ с корректной подписьюотвергнуто: lie
портит один бит тегаотвергнуто: tag
отвечает nonce, которого прогон не выдавалотвергнуто: fabricated
отвечает от имени другого узлаотвергнуто: node
теряет ответотвергнуто: short read, прогон останавливается
ключ так и не установлен (статус 0x04)не засчитано, хотя y верный

После прогона на плате, описанного ниже, добавлена восьмая проверка, уже для канала, а не для ячейки. Это поток, из середины которого пропадают 16 байт, как было в канале платы. Харнесс засчитывает ответы до дыры, отвергает повреждённый ответ и останавливается.

Те же байты через RTL

Собственные исходники ячейки, trinet_node_core.v и trinet_siphash24.v, без изменений, симулированы в Icarus Verilog на уровне битов UART. Ключ ставился по проводу ровно так же, как на плате. Веса — случайные тернарные значения точно в формах tern_tc, потому что файла обученной модели в этом окружении нет.

Прогон RTLЗадачСтрок бит-точноКвитанций проверено
Слой 0, все семь матриц, w_down по 27 кускам67 2005 632 / 5 63267 200 / 67 200
Слой 5, w_down, int8-активации (6 разрядов)51 840320 / 32051 840 / 51 840
Слой 0, wk, ключ не установлен6400 / 640 / 640 (статус 0x04)
Слой 0, wk, проверка под другим ключом1 2800 / 1280 / 1 280

Исправленный харнесс на плате

27 сентября 2026 года исправленный харнесс прогнали на AX7203 с файлом обученной модели. Хостом был M1 Pro, связанный с UART платы (CP2102N) через USB-хаб. Векторы x — случайные тестовые векторы из фиксированного seed, а не настоящие активации модели.

Прогон на плате (1 144 744 бод)Задач в полётеКвитанций провереноСтрок бит-точноИтог
Случайный тернарный matvec 320x320643 200 / 3 200320 / 320PASS, 0,79 с
Все 42 матрицы, тернарный x6418 984 из 403 200, потом канал потерял 16 байт1 898 / 33 792FAIL, остановлен
Слой 5, w_down, int8 x649 886 из 51 840, потом канал потерял 4 байта61 / 320FAIL, остановлен
Слой 5, w_down, int8 x851 840 / 51 840320 / 320PASS, 11,9 с
Все 42 матрицы, тернарный x24403 200 / 403 20033 792 / 33 792PASS, 85,5 с
Все 42 матрицы, тернарный x (контроль)646 679 из 403 200, потом канал потерял 59 байт667 / 33 792FAIL, остановлен
Все 42 матрицы, тернарный x (зарегистрирован)26403 200 / 403 20033 792 / 33 792PASS, 85,3 с
Все 42 матрицы, тернарный x (зарегистрирован)30149 986 из 403 200, потом канал потерял 7 байт12 822 / 33 792FAIL, остановлен

Строка с окном 24 — результат, которого ждал этот пост. Все 42 тернарные матрицы обученной модели, все 6 451 200 весов, отработали на текущем битстриме. Хост проверил каждую из 403 200 квитанций: статус, nonce, id узла, тег SipHash, пересчитанный под ключом, и y. Все 33 792 строки совпали с оракулом по int8-весам. Прогон занял 85,5 с, 4 716 ответов в секунду.

Прогон w_down при 8 задачах в полёте — первый прогон обученных весов с int8-активациями на плате, в котором проверена каждая квитанция: 51 840 тегов и 320 строк. Это одна матрица из 42 и один случайный вектор активаций.

Все четыре провала — не неверные ответы. У всех 185 535 ответов, пришедших целыми до дыры, был верный y и сходящийся тег. Ответ, который рвала каждая дыра, не прошёл проверку и был отвергнут. Сломался канал. Из потока ответов пропадало 16, 4, 59 и 7 байт, и по обе стороны дыры байты целые. Ячейка такую картину дать не может: каждый ответ она отправляет целиком из одного буфера. Харнесс сделал то, что должен: остановился на первом нераспознанном кадре и после него ничего не засчитал.

Окно имеет значение, и это показал контроль. Тот же полный прогон на том же харнессе, установке и сессии прошёл чисто при 24 задачах в полёте и потерял байты после 6 744 задач при 64. Значит, успех дало окно, а не замер времени и вывод hex, добавленные в новый харнесс. При 64 три длинных прогона сорвались три раза.

Первое объяснение контроль не выдержало. Предполагалось, что ответы копятся, пока процесс харнесса отвлёкся. Исправленный харнесс эти паузы измеряет. В провальном контроле при 64 самая долгая была 4,2 мс, примерно за 1 700 ответов до дыры. Успешные прогоны пережили паузы 22,4 мс при 24 задачах в полёте и 25,8 мс при 26.

Зато подходит USB-мост. UART платы идёт к хосту через CP2102N; по его даташиту у него приёмный буфер на 512 байт, а выше 1 Мбод нужно аппаратное управление потоком, иначе приёмник переполняется. Узел работает на 1 144 744 бод, и линий управления потоком у него нет, так что придержать его нечем. При W задачах в полёте к хосту идёт не больше 19 x W байт ответов. Следующую проверку зарегистрировали до запуска: при 26 задачах в полёте (494 байта) — успех, при 30 (570 байт) — срыв. Так и вышло. Окно 26 провело все 403 200 задач, окно 30 потеряло 7 байт после 150 017 задач. Порог потерь лежит между 494 и 570 байтами, около буфера на 512.

Одно побочное предсказание не сбылось. Срыв при 30 пришёл после 150 017 задач, а не в первые 20 000, как при 64, так что задержки, заполняющие буфер, реже, чем объяснил бы один фиксированный простой. Что задерживает USB-передачи моста, не измерено, и хаб не исключён. Правило харнесса теперь — держать 19 x W меньше 512 байт, поэтому по умолчанию окно 24 (456 байт). Скорости это заметно не стоит: 4 716 ответов в секунду против примерно 4 660–4 750 при 64 до сбоев.

Две задачи, записанные в новое железо, его не требуют

w_down пропустили, потому что у неё вход шириной 864, и по плану для неё нужна была ячейка шире. Не нужна: 864 = 27 x 32, то есть это 27 задач на строку на той же ячейке. У wk и wv вход шириной 320, их просто пропустили. Вместе это все 42 тернарные матрицы и все 6 451 200 весов на текущем битстриме.

int8-активации числились за Stage B.3, где планировался новый RTL. Любое целое от -364 до 364 — это сумма шести сбалансированно-троичных разрядов: q = сумма 3^k d_k, где каждый d_k из {-1, 0, +1}. Значит, w.q = сумма 3^k (w.d_k), и каждое w.d_k — обычная тернарная задача. Шесть задач на кусок заменяют новый тракт данных. Ячейка остаётся тернарной, а степени тройки хост применяет открыто.

Чем это не является

Дальше

  1. Снять ограничение канала: добавить узлу управление потоком RTS/CTS, как требует даташит CP2102N выше 1 Мбод. До тех пор держать ответы в полёте меньше 512 байт, как теперь делает харнесс.
  2. Реальные активации: выгрузить int8-входы, которые tc_infer считает для настоящего промпта, и прогнать с ними целый слой.
  3. Уйти с UART: перейти на Ethernet или USB FIFO и измерить это на плате.
  4. Сделать квитанции проверяемыми для всех: публиковать корень Меркла каждого прогона и добавить случайное перевычисление или проверки Фрейвалдса. Для тернарных matvec и то и другое стоит дёшево.
  5. Измерить мощность от лабораторного блока питания. Без этого публиковать сравнение по энергии нечем.

Чего это не решает

Пруфы

Поработаем вместе

Хотите так же проверить собственный дизайн?

Я аудирую RTL и строю независимые побитово точные модели, затем провожу результат через синтез и, когда это полезно, проверяю на плате Artix-7. Первый модуль проверки — бесплатно.