From 647ead892c84a7e76ac5e351da7cb357c5c83736 Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Fri, 9 Oct 2026 14:54:05 +0700 Subject: [PATCH] blog: three dies, one verdict -- R3-3 makes a silicon check something you run once (Closes #1568) The owner asked (2026-10-09, translated): an X post and a blog post about R3-3, and what it gives for build speed for everyone. The post, EN and RU: specs/fpga/ternary_link.t27 checked on three distinct dies (A 050d58218fd9854, B 0389c0c2d85e85c, C 050a5824d85e85c) on three machines, die C driven over USB/IP from 410 ms away; run-record INDEP_DIES, citable; die B found the per-die DNA tail bug (t27#7761, t27c 0.5.1). The speed section ties a citable silicon verdict to the five-part reuse rule of specs/verified/reuse.t27, and says plainly what is not done: one spec so far, CI does not yet skip a silicon check by receipt, INDEP_DIES not INDEP_OPERATORS, DNA is not a secret. Files: bodies/three-dies-one-verdict.ts, its index entry, its registration in posts.ts. Closes #1568 Co-Authored-By: Claude Opus 5.5 --- .../blog/bodies/three-dies-one-verdict.ts | 95 +++++++++++++++++++ apps/website/src/data/blog/index.ts | 35 +++++++ apps/website/src/data/blog/posts.ts | 2 + 3 files changed, 132 insertions(+) create mode 100644 apps/website/src/data/blog/bodies/three-dies-one-verdict.ts diff --git a/apps/website/src/data/blog/bodies/three-dies-one-verdict.ts b/apps/website/src/data/blog/bodies/three-dies-one-verdict.ts new file mode 100644 index 0000000000..55e43f88e3 --- /dev/null +++ b/apps/website/src/data/blog/bodies/three-dies-one-verdict.ts @@ -0,0 +1,95 @@ +import type { Block } from '../types' + +// Numbers here come from the three R3-3 receipts on gHashTag/t27 master (#7816 die A, #7759 +// die B, #7906 die C), from `t27c run-record --challenge e61798b78cbb6b287b8ff8523cea71a6 +// specs/fpga/ternary_link.t27` on master 8eb0ff41c with the t27c-v0.5.1 build, from the die A +// run log of 2026-10-08 (nextpnr 60.75 s), from t27#7669 and t27#7761, and from +// specs/verified/reuse.t27 and specs/ci/affected.t27. + +export const body: Block[] = [ + { + kind: 'p', + text: 'One t27 spec has now been checked on three different FPGA chips, on three machines, and the three answers agree. Each chip is named by its own device DNA. Each signed its answer to the same one-time challenge. Our run-record tool calls the result citable. This is R3-3, and it changes what a hardware check costs: it becomes something you run once per version and then reuse, instead of something every build repeats.', + }, + { kind: 'h', text: 'What was run' }, + { + kind: 'p', + text: 'The spec is specs/fpga/ternary_link.t27. t27c 0.5.1 turns it into a bitstream with the open toolchain (yosys, nextpnr-xilinx, prjxray), loads it into the FPGA\'s SRAM, and reads the answer back over JTAG: 0xa5a532bd, all four clauses true, ok=1. Before that, every run loads a bitstream built for the wrong part, and the chip must refuse it (Done 0). That control stops a board that answers yes to everything from passing.', + }, + { + kind: 'p', + text: '[measured] Die A, 050d58218fd9854, is on the owner\'s bench. Die B, 0389c0c2d85e85c, is on operator B\'s bench. Die C, 050a5824d85e85c, is on Phil\'s PC. All three answered the verifier\'s challenge e61798b7…, and the key of the machine that ran each one signed its receipt. run-record reads the three receipts in seconds: run complete, all fresh, every die named, independence INDEP_DIES, citable.', + }, + { kind: 'h', text: 'Die C was driven from 410 ms away' }, + { + kind: 'p', + text: 'Phil\'s board sits on a Windows PC with no FPGA toolchain. Instead of building one there, operator B\'s Linux lab borrowed only his JTAG cable, over USB/IP inside a private Tailscale network. Phil, his own Claude and the owner all agreed to this in the open first (t27#7669). The bitstream took about 70 minutes to load at a 410 ms round trip, and the whole run took about 2.5 hours. The first attempt found an empty JTAG chain: the board had USB power but not its 12 V supply.', + }, + { kind: 'h', text: 'A second die found a bug the first one could not' }, + { + kind: 'p', + text: 'Die B read its DNA, but its receipt came back without one. Our spec expected the last 7 bits of the fuse copy of the DNA to be 0x4F. That was true of die A only: die B has 0x37. One die cannot show that a constant is really per-die. The fix (t27#7761) became t27c 0.5.1, and all three receipts were made with it.', + }, + { kind: 'h', text: 'Why this makes builds faster for everyone' }, + { + kind: 'p', + text: 'specs/verified/reuse.t27 already says when a built artifact may be reused instead of rebuilt. Five parts must match: the spec\'s bytes, the seals of everything it imports, the toolchain, the build configuration, and a PASS verdict. A part that was never recorded counts as different. For software this already pays off: CI tests only the specs a change can affect (specs/ci/affected.t27).', + }, + { + kind: 'p', + text: 'For hardware, the weak part was the fifth one. A PASS on one board might be a lucky board, a mislabelled bench or a bug that only one chip hides, as die B just showed. R3-3 is the first hardware verdict strong enough to be that fifth part: three distinct dies, fresh signed receipts, and a run record that a verdict may cite.', + }, + { + kind: 'p', + text: '[measured] On a local bench, a silicon check of ternary_link costs about a minute of place-and-route (60.75 s on die A) plus the load. Remotely it took hours. Once the verdict is citable, the check has to run once per spec version and toolchain, not once per build, per machine or per contributor. Everyone else checks the receipts, which takes seconds. As more specs get runs like this one, the cost of verifying a change grows with what the change touches, not with the size of the corpus.', + }, + { kind: 'h', text: 'What is not done' }, + { + kind: 'p', + text: 'There is one spec so far. CI does not yet skip a silicon check by citing a receipt: wiring reuse.t27 into the pipeline is the next step. Dies B and C were signed with keys from one operator\'s machines, so the run reaches INDEP_DIES, not INDEP_OPERATORS. And a device DNA is a name, not a secret, so no receipt here is rooted in the device itself.', + }, +] + +export const ruBody: Block[] = [ + { + kind: 'p', + text: 'Одна спецификация t27 теперь проверена на трёх разных кристаллах ПЛИС, на трёх машинах, и все три ответа совпали. Каждый кристалл назван своим заводским DNA. Каждый подписал ответ на один и тот же одноразовый запрос. Наша утилита run-record признаёт результат пригодным для ссылок. Это R3-3, и он меняет цену проверки на железе: её можно прогнать один раз на версию и дальше использовать готовый результат, а не повторять в каждой сборке.', + }, + { kind: 'h', text: 'Что прогоняли' }, + { + kind: 'p', + text: 'Спецификация — specs/fpga/ternary_link.t27. t27c 0.5.1 превращает её в битстрим открытой цепочкой (yosys, nextpnr-xilinx, prjxray), загружает в SRAM ПЛИС и читает ответ по JTAG: 0xa5a532bd, все четыре условия истинны, ok=1. Перед этим каждый прогон загружает битстрим для чужого чипа, и кристалл обязан его отвергнуть (Done 0). Эта проверка не даёт пройти плате, которая отвечает «да» на всё подряд.', + }, + { + kind: 'p', + text: '[измерено] Кристалл A, 050d58218fd9854, стоит на стенде владельца. Кристалл B, 0389c0c2d85e85c, — на стенде оператора B. Кристалл C, 050a5824d85e85c, — на ПК Фила. Все три ответили на запрос проверяющего e61798b7…, и каждую квитанцию подписал ключ машины, которая вела прогон. run-record читает три квитанции за секунды: прогон полный, все свежие, каждый кристалл назван, независимость INDEP_DIES, на прогон можно ссылаться.', + }, + { kind: 'h', text: 'Кристаллом C управляли за 410 мс' }, + { + kind: 'p', + text: 'Плата Фила стоит на ПК с Windows без инструментов для ПЛИС. Вместо того чтобы ставить их там, Linux-лаборатория оператора B взяла только его JTAG-кабель, по USB/IP внутри закрытой сети Tailscale. Сначала на это открыто согласились Фил, его собственный Claude и владелец (t27#7669). При задержке 410 мс загрузка битстрима шла около 70 минут, весь прогон — около 2,5 часа. Первая попытка увидела пустую JTAG-цепочку: плата была подключена по USB, но без блока питания на 12 В.', + }, + { kind: 'h', text: 'Второй кристалл нашёл ошибку, которую первый найти не мог' }, + { + kind: 'p', + text: 'Кристалл B прочитал свой DNA, но квитанция пришла без него. Наша спецификация ждала, что последние 7 бит DNA в копии из eFUSE равны 0x4F. Это было верно только для кристалла A, у B там 0x37. По одному кристаллу нельзя увидеть, что константа на самом деле своя у каждого кристалла. Исправление (t27#7761) вошло в t27c 0.5.1, и все три квитанции сделаны уже им.', + }, + { kind: 'h', text: 'Почему это ускоряет сборку для всех' }, + { + kind: 'p', + text: 'В specs/verified/reuse.t27 уже записано, когда собранный артефакт можно взять готовым, а не пересобирать. Должны совпасть пять частей: байты спецификации, печати всего, что она импортирует, инструменты, настройки сборки и вердикт PASS. Часть, которая не была записана, считается отличающейся. Для программной части это уже работает: CI тестирует только те спецификации, которые может задеть изменение (specs/ci/affected.t27).', + }, + { + kind: 'p', + text: 'Для железа слабым местом была пятая часть. PASS на одной плате мог означать удачную плату, перепутанный стенд или ошибку, которую скрывает только один кристалл, как только что показал кристалл B. R3-3 — первый вердикт на железе, достаточно сильный для этой пятой части: три разных кристалла, свежие подписанные квитанции и запись о прогоне, на которую может сослаться вердикт.', + }, + { + kind: 'p', + text: '[измерено] На местном стенде проверка ternary_link на кремнии стоит около минуты размещения и трассировки (60,75 с на кристалле A) плюс загрузка. Удалённо она заняла часы. Когда вердикт пригоден для ссылок, проверку нужно прогнать один раз на версию спецификации и инструментов, а не в каждой сборке, на каждой машине и у каждого участника. Остальные проверяют квитанции, а это секунды. Чем больше спецификаций получат такие прогоны, тем сильнее цена проверки изменения будет зависеть от того, что изменение задевает, а не от размера всего корпуса.', + }, + { kind: 'h', text: 'Чего пока нет' }, + { + kind: 'p', + text: 'Пока это одна спецификация. CI ещё не пропускает проверку на кремнии по ссылке на квитанцию: подключить reuse.t27 к конвейеру — следующий шаг. Кристаллы B и C подписаны ключами с машин одного оператора, поэтому прогон достигает INDEP_DIES, а не INDEP_OPERATORS. И DNA кристалла — это имя, а не секрет, так что ни одна квитанция здесь не опирается на сам кристалл как на корень доверия.', + }, +] diff --git a/apps/website/src/data/blog/index.ts b/apps/website/src/data/blog/index.ts index 28a53b1ba0..1638fce42b 100644 --- a/apps/website/src/data/blog/index.ts +++ b/apps/website/src/data/blog/index.ts @@ -2,6 +2,41 @@ import type { PostMeta } from './types' /** Индекс блога: список и метаданные без тяжёлых тел публикаций. */ export const postsIndex: PostMeta[] = [ + { + slug: "three-dies-one-verdict", + title: "Three dies, one verdict: a silicon check you run once", + summary: "[one spec so far; two of the three receipts share an operator; CI does not yet skip silicon checks by receipt] One t27 spec, ternary_link, was checked on three different FPGA chips on three machines, one of them driven from 410 ms away, and the answers agree. Each chip is named by its device DNA and signed its answer to one challenge; run-record calls the run citable (INDEP_DIES). A second chip found a bug the first could not. The point: with a citable hardware verdict, the rule in reuse.t27 lets a silicon check run once per spec version and toolchain, and everyone else check the receipts in seconds.", + date: "2026-10-09", + readingMinutes: 5, + tags: ["t27", "FPGA", "Verification"], + receipts: [ + { label: "t27#7669: R3-3, the run and its run-record summary", href: "https://github.com/gHashTag/t27/issues/7669" }, + { label: "t27#7816: receipt from die A 050d58218fd9854", href: "https://github.com/gHashTag/t27/pull/7816" }, + { label: "t27#7759: receipt from die B 0389c0c2d85e85c", href: "https://github.com/gHashTag/t27/pull/7759" }, + { label: "t27#7906: receipt from die C 050a5824d85e85c, run over USB/IP", href: "https://github.com/gHashTag/t27/pull/7906" }, + { label: "t27#7761: the DNA tail differs per die, found by die B", href: "https://github.com/gHashTag/t27/pull/7761" }, + { label: "specs/verified/reuse.t27: when an artifact is reused instead of rebuilt", href: "https://github.com/gHashTag/t27/blob/master/specs/verified/reuse.t27" }, + { label: "specs/fpga/ternary_link.t27: the spec that ran", href: "https://github.com/gHashTag/t27/blob/master/specs/fpga/ternary_link.t27" }, + { label: "trinity#1568: this post's issue", href: "https://github.com/gHashTag/trinity/issues/1568" }, + ], + openQuestions: [ + "One spec has a three-die run so far; the speed-up grows only as more specs get one.", + "CI does not yet skip a silicon check by citing a receipt; wiring reuse.t27 into the pipeline is the next step.", + "Dies B and C were signed with keys from one operator's machines, so the run is INDEP_DIES, not INDEP_OPERATORS.", + "A device DNA is a name, not a secret, so no receipt is rooted in the device itself.", + ], + published: true, + ru: { + title: "Три кристалла, один вердикт: проверка на кремнии, которую прогоняют один раз", + summary: "[пока одна спецификация; две квитанции из трёх у одного оператора; CI ещё не пропускает проверку на кремнии по квитанции] Одна спецификация t27, ternary_link, проверена на трёх разных кристаллах ПЛИС на трёх машинах, одной из них управляли за 410 мс, и ответы совпали. Каждый кристалл назван своим DNA и подписал ответ на один запрос; run-record признаёт прогон пригодным для ссылок (INDEP_DIES). Второй кристалл нашёл ошибку, которую первый найти не мог. Главное: когда на вердикт на железе можно сослаться, правило из reuse.t27 позволяет прогнать проверку один раз на версию спецификации и инструментов, а остальным проверить квитанции за секунды.", + openQuestions: [ + "Пока прогон на трёх кристаллах есть у одной спецификации; ускорение растёт, только когда такие прогоны появятся у других.", + "CI ещё не пропускает проверку на кремнии по ссылке на квитанцию; подключить reuse.t27 к конвейеру — следующий шаг.", + "Кристаллы B и C подписаны ключами с машин одного оператора, поэтому прогон INDEP_DIES, а не INDEP_OPERATORS.", + "DNA кристалла — имя, а не секрет, поэтому ни одна квитанция не опирается на сам кристалл как на корень доверия.", + ], + }, + }, { slug: "an-eye-on-the-board", title: "An eye on the board: a camera found the wrong board, and a t27 spec put the right one on the network", diff --git a/apps/website/src/data/blog/posts.ts b/apps/website/src/data/blog/posts.ts index 40eb7a2b01..4fd1105410 100644 --- a/apps/website/src/data/blog/posts.ts +++ b/apps/website/src/data/blog/posts.ts @@ -1,3 +1,4 @@ +import { body as body_three_dies_one_verdict, ruBody as ruBody_three_dies_one_verdict } from './bodies/three-dies-one-verdict' import { body as body_an_eye_on_the_board, ruBody as ruBody_an_eye_on_the_board } from './bodies/an-eye-on-the-board' import { body as body_course_three_goldenfloat, ruBody as ruBody_course_three_goldenfloat } from './bodies/course-three-goldenfloat' import { body as body_nine_modules_of_three_again, ruBody as ruBody_nine_modules_of_three_again } from './bodies/nine-modules-of-three-again' @@ -103,6 +104,7 @@ import { body as body_features_that_change_no_bits, ruBody as ruBody_features_th import { body as body_one_commit_nine_workflow_outcomes, ruBody as ruBody_one_commit_nine_workflow_outcomes } from './bodies/one-commit-nine-workflow-outcomes' const bodies: Record = { + 'three-dies-one-verdict': { body: body_three_dies_one_verdict, ruBody: ruBody_three_dies_one_verdict }, 'an-eye-on-the-board': { body: body_an_eye_on_the_board, ruBody: ruBody_an_eye_on_the_board }, 'buses-and-peripherals': { body: body_course_buses_and_peripherals, ruBody: ruBody_course_buses_and_peripherals }, 'clocks-resets-and-cdc': { body: body_clocks_resets_and_cdc, ruBody: ruBody_clocks_resets_and_cdc },