diff --git a/apps/website/i18n/course.ru.json b/apps/website/i18n/course.ru.json index 5efa2968ad..2839ef5fef 100644 --- a/apps/website/i18n/course.ru.json +++ b/apps/website/i18n/course.ru.json @@ -4,10 +4,10 @@ "$note": "Russian words for specs/course/course.t27. Keys are MODULE_IDS, LESSON_IDS and, under ui, the SAY_ names. Numbers and {placeholders} must match the English field; course-from-spec.mjs checks both.", "ui": { "TITLE": "С нуля до чипа: программирование FPGA на t27", - "DESCRIPTION": "27 уроков, в каждом рабочий виджет: что такое FPGA, как числа становятся битами, ваша спека на t27, как её компилирует t27c и как она превращается в битстрим на настоящей плате. Затем лаборатория из 3 уроков о наших собственных исследованиях.", + "DESCRIPTION": "27 уроков, в каждом рабочий виджет: что такое FPGA, как числа становятся битами, ваша спека на t27, как её компилирует t27c и как она превращается в битстрим на настоящей плате. Затем лаборатория из 3 уроков о наших собственных исследованиях и 3 урока о форматах чисел MX, которыми пользуются ИИ-чипы.", "KICKER": "Курс", "LEAD": "Опыт с железом не нужен. Каждый урок открывает настоящий инструмент, собранный из реальных запусков на нашем стенде: прочитайте немного и попробуйте сами.", - "SHAPE": "9 модулей по 3 урока, 27 клеток, затем лаборатория из 3. Закрашенная клетка — урок, который вы отметили пройденным.", + "SHAPE": "9 модулей по 3 урока, 27 клеток, затем лаборатория из 3 и модуль ИИ-чисел из 3. Закрашенная клетка — урок, который вы отметили пройденным.", "START": "Начать урок 1", "CONTINUE": "Продолжить", "MODULE": "Модуль {0}", @@ -45,7 +45,8 @@ "timing": { "TITLE": "Размещение, трассировка, тайминг", "LINE": "Где ячейки оказываются на кристалле и успевает ли тактовая частота." }, "bitstream": { "TITLE": "Битстрим", "LINE": "Как оттрассированный дизайн становится битами, которые загружает чип, без Vivado." }, "board": { "TITLE": "На плате", "LINE": "Спросите чип, кто он, загрузите биты и проверьте их." }, - "lab": { "TITLE": "Лаборатория: наши исследования", "LINE": "Свой формат чисел, честная таблица результатов и таблицы модели, перемноженные на плате." } + "lab": { "TITLE": "Лаборатория: наши исследования", "LINE": "Свой формат чисел, честная таблица результатов и таблицы модели, перемноженные на плате." }, + "mx": { "TITLE": "ИИ-числа: блок MX", "LINE": "Как ИИ-чипы хранят веса в нескольких битах: один общий масштаб на блок, сам байт масштаба и что один выброс делает с соседями." } }, "lessons": { "fpga-is-a-grid": { @@ -227,6 +228,24 @@ "GOAL": "Как компьютер кормит плату работой через последовательный мост, почему важен размер окна и что плата посчитала.", "TEXT": "Арифметику делает плата; компьютер отправляет ей работу через последовательный USB-мост. Ответы платы ждут в маленьком приёмном буфере микросхемы моста, и если держать в полёте больше запросов, чем помещается их ответов, ответы теряются. Окно поменьше убрало потери и не замедлило прогон. По этой линии плата перемножила все троичные матрицы весов небольшой языковой модели, и каждая строка совпала с эталоном бит в бит. Сам текст генерирует Mac, который делает всё, кроме троичных скалярных произведений: плата — это калькулятор, а не вся модель.", "TASK": "Тяните окно, пока ведро не переполнится, и запомните самое большое окно, которое помещается; затем сравните два настоящих прогона." + }, + "one-scale-per-block": { + "TITLE": "Один масштаб на блок весов", + "GOAL": "Почему ИИ-форматы делят один масштаб на блок весов и как читается байт масштаба OCP MX.", + "TEXT": "У языковой модели миллионы весов, и хранить и пересылать полное число с плавающей точкой для каждого дорого. Форматы Microscaling (MX) консорциума Open Compute Project хранят каждый вес в 4, 6 или 8 битах и дают каждому блоку из 32 весов один общий масштаб. Этот масштаб — один байт, E8M0: без знака и без мантиссы, только порядок, так что код e означает 2 в степени e минус 127, а код 255 означает NaN. Плеер компилирует e8m0.t27, спеку этого байта, прямо в вашем браузере и запускает её тесты. Проверку, которую браузер выполнить не может, он пропускает и объясняет почему.", + "TASK": "Запустите тесты и посчитайте пропуски; затем откройте Verilog и найдите функцию, которая проверяет код 255." + }, + "scale-byte-native": { + "TITLE": "Байт масштаба на настоящей машине", + "GOAL": "Что нативный компилятор проверяет в той же спеке, включая то, что браузер пропустил.", + "TEXT": "Плеер в прошлом уроке не смог выполнить все проверки в e8m0.t27 и прямо об этом сказал. Эта запись запускает нативный t27c и Zig на настоящей машине на той же спеке: её константы, отчёт о тестах, тесты Zig и Verilog для проверки на NaN. Часть проверок — инварианты, которые компилятор доказывает во время компиляции, так что компиляция и есть проверка. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "TASK": "Найдите, сколько инвариантов отчёт доказал во время компиляции, и сравните число пройденных тестов с числом у плеера." + }, + "one-outlier": { + "TITLE": "Один выброс, много нулей", + "GOAL": "Чего стоит общий масштаб: как один крупный вес обнуляет мелкие веса своего блока, формат за форматом.", + "TEXT": "Общий масштаб задаёт самое большое значение в блоке, поэтому один выброс задаёт его для всех 32 весов. Веса, намного меньшие выброса, падают ниже самого мелкого шага, который может показать элемент, и записываются нулём. Эта запись нативно запускает ocp_mx.t27, нашу спеку OCP MX v1.0, на блоке из 31 мелкого веса и одного выброса и считает, сколько весов обнуляет каждый формат элемента. Затем она подкладывает ошибку, одно изменённое смещение порядка, и падает ровно один тест: спека, которая несёт свои тесты, ловит собственные ошибки.", + "TASK": "Найдите, какой формат элемента обнуляет больше всего мелких весов, а какие два — ни одного; затем найдите тест, который ломает подложенная ошибка." } } } diff --git a/apps/website/public/learn/a-build-receipt/card.png b/apps/website/public/learn/a-build-receipt/card.png index 7a8d43e94b..7ddccc41cd 100644 Binary files a/apps/website/public/learn/a-build-receipt/card.png and b/apps/website/public/learn/a-build-receipt/card.png differ diff --git a/apps/website/public/learn/a-build-receipt/index.html b/apps/website/public/learn/a-build-receipt/index.html index 7530c1a17f..990d69434b 100644 --- a/apps/website/public/learn/a-build-receipt/index.html +++ b/apps/website/public/learn/a-build-receipt/index.html @@ -4,7 +4,7 @@ -A build, itemised: FPGA course, lesson 23 of 30 +A build, itemised: FPGA course, lesson 23 of 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + +
t27.aiРусский
+
+ +

One outlier, many zeros

+

You will learn

What a shared scale costs: how one large weight pushes the small weights of its block to zero, format by format.

+

A shared scale is set by the largest value in its block, so one outlier sets it for all 32 weights. Weights much smaller than the outlier then fall below the smallest step the element can show and are stored as zero. This recording runs ocp_mx.t27, our spec of OCP MX v1.0, natively on a block of 31 small weights and one outlier, and counts how many each element format flushes to zero. Then it plants a bug, one changed exponent bias, and exactly one test fails: a spec that carries its tests catches its own mistakes.

+

Try it

Read which element format flushes the most small weights to zero and which two flush none; then find the test the planted bug breaks.

+

Open the interactive lesson →

+
t27c on ocp_mx.t27 -- OCP MX v1.0, native
t27c on ocp_mx.t27 -- OCP MX v1.0, native ↗
+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway), spec at t27 1a3786657: 49 tests pass natively; one changed bias makes exactly one test fail; git restores the spec.

+ + +

All lessons

+ +
+ + diff --git a/apps/website/public/learn/one-scale-per-block/card.png b/apps/website/public/learn/one-scale-per-block/card.png new file mode 100644 index 0000000000..7fde52d35c Binary files /dev/null and b/apps/website/public/learn/one-scale-per-block/card.png differ diff --git a/apps/website/public/learn/one-scale-per-block/index.html b/apps/website/public/learn/one-scale-per-block/index.html new file mode 100644 index 0000000000..d2529bddd7 --- /dev/null +++ b/apps/website/public/learn/one-scale-per-block/index.html @@ -0,0 +1,73 @@ + + + + + + +One scale for a block of weights: FPGA course, lesson 31 of 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
t27.aiРусский
+
+ +

One scale for a block of weights

+

You will learn

Why AI formats share one scale across a block of weights, and how the scale byte of OCP MX is read.

+

A language model has millions of weights, and a full float for each is expensive to store and to move. The Microscaling (MX) formats of the Open Compute Project keep each weight in 4, 6 or 8 bits and give every block of 32 weights one shared scale. That scale is a single byte called E8M0: no sign and no mantissa, only an exponent, so code e means 2 to the power e minus 127, and code 255 means NaN. The player compiles e8m0.t27, the spec of that byte, in your browser and runs its tests. Where the browser cannot run a check, it skips it and says why.

+

Try it

Run the tests and count the skips; then open the Verilog and find the function that checks for code 255.

+

Open the interactive lesson →

+
e8m0.t27, the OCP MX scale byte, compiled inside the post
e8m0.t27, the OCP MX scale byte, compiled inside the post ↗
+

The OCP MX shared scale as a t27 spec, compiled by t27c as WebAssembly in your browser: one byte, 2^(e-127), code 255 is NaN. Seven backends, the spec's own tests and its Verilog.

+ + +

All lessons

+ +
+ + diff --git a/apps/website/public/learn/pins-and-package/card.png b/apps/website/public/learn/pins-and-package/card.png index 7725bb905b..bfc3768c41 100644 Binary files a/apps/website/public/learn/pins-and-package/card.png and b/apps/website/public/learn/pins-and-package/card.png differ diff --git a/apps/website/public/learn/pins-and-package/index.html b/apps/website/public/learn/pins-and-package/index.html index 6bf430b8f6..67d9165c46 100644 --- a/apps/website/public/learn/pins-and-package/index.html +++ b/apps/website/public/learn/pins-and-package/index.html @@ -4,7 +4,7 @@ -Pins, balls and the package: FPGA course, lesson 3 of 30 +Pins, balls and the package: FPGA course, lesson 3 of 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + +
t27.aiРусский
+
+ +

The scale byte on a real machine

+

You will learn

What the native compiler checks in the same spec, including what the browser skipped.

+

The player in the last lesson could not run every check in e8m0.t27, and it said so. This recording runs the native t27c and Zig on a real machine on the same spec: its constants, the test report, the Zig tests, and the Verilog for the NaN check. Some checks are invariants the compiler proves while it compiles, so compiling is the check. Every byte in the recording was printed by the command; only the typing is staged.

+

Try it

Find how many invariants the test report proved at compile time, and compare its pass count with the player's.

+

Open the interactive lesson →

+
t27c on e8m0.t27 -- the MX shared scale byte, native
t27c on e8m0.t27 -- the MX shared scale byte, native ↗
+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway): the E8M0 scale byte of OCP MX, 18 tests and 9 comptime invariants pass, and the Verilog backend's NaN check.

+

specs/numeric/e8m0.t27

// SPDX-License-Identifier: Apache-2.0
+// t27/specs/numeric/e8m0.t27
+// E8M0 -- the OCP Microscaling shared scale, and the trit count it does not have.
+//
+// WHY THIS EXISTS. `specs/numeric/formats_catalog.t27` is the numeric SSOT and it
+// names `e8m0` FIVE times as the storage of another format --
+//
+//     mxfp8   storage=u8_plus_shared_e8m0
+//     mxfp6   storage=u8_packed_plus_e8m0
+//     mxfp4   storage=u8_packed_plus_e8m0
+//     mxgf6   storage=u8_packed_plus_e8m0
+//     mxgf4   storage=u8_packed_plus_e8m0
+//
+// -- and `grep "id=e8m0 "` over the same file returns ZERO. The SSOT depends on a
+// component it never defines. This file defines it. Measured 2026-08-16.
+//
+// LAYOUT.  E8M0 = [ exponent(8) ].  No sign. No mantissa. No implicit one.
+//
+//     value = 2^(E - 127),   E in [0, 254]
+//     E = 255                is NaN
+//
+// Source: OCP Microscaling Formats (MX) Specification v1.0, Rouhani et al. 2023,
+// arXiv:2310.10537 -- the citation the catalogue already carries for every one of
+// the five dependants.
+//
+// THE RESULT THIS MIGRATION PRODUCED, which is why it is worth a spec and not a
+// constant. E8M0's exponent takes 255 distinct finite values, e in [-127, +127].
+// Five balanced trits span 3^5 = 243 and six span 3^6 = 729, so
+//
+//     243 < 255 < 729
+//
+// and E8M0's exponent field is NOT a whole number of trits. It needs six and
+// uses 255 of 729 -- 35 percent. Contrast `specs/numeric/tnf17.t27`, whose
+// offset field holds exactly 81 = 3^4 values and uses 81 of 81. That contrast is
+// stated below as a comptime invariant rather than as prose, because it is the
+// only thing this format tells us that its own standard does not.
+//
+// WHAT THIS IS NOT. E8M0 is a SHARED SCALE, applied once per block of elements,
+// not a weight code. Under the golden sieve (specs/numeric/golden_sieve.t27) it
+// is not a candidate at all: S1 asks |A| = 3^k and 255 is not a power of three.
+// It is also, for the same reason it is cheap, exempt from the sieve's concerns
+// -- a pure power of two applied to a whole block is one shift, one lane, no
+// DSP, and it never enters a neuron's truth table.
+//
+// phi^2 + phi^-2 = 3 | TRINITY
+
+module E8M0 {
+
+    // ---- Field geometry, from the OCP MX v1.0 specification ----
+
+    const E8M0_BITS       : u32 = 8;
+    const E8M0_BIAS       : i32 = 127;   // code 127 == 2^0
+    const E8M0_NAN_CODE   : u32 = 255;   // the one reserved code
+    const E8M0_MAX_FINITE : u32 = 254;   // largest finite code, 2^127
+    const E8M0_MIN_FINITE : u32 = 0;     // smallest finite code, 2^-127
+    const E8M0_ONE        : u32 = 127;   // the code whose value is 1.0
+
+    // Distinct finite values the field can hold: 0..254 inclusive.
+    const E8M0_FINITE_CODES : u32 = 255;
+
+    // Trit spans, written out because the numeric core has no exponentiation.
+    //
+    // W781 DEFECT, caught by the comptime invariant that is the reason this file
+    // has invariants at all: the first version of this table started at 3^5, so
+    // `trits_needed(81)` answered 5 and `packs_exactly(81)` came back FALSE for a
+    // number that is exactly 3^4. The table must reach DOWN to every power a
+    // caller might land on, not only up to the one the subject format needs.
+    const THREE_POW_3 : u32 = 27;
+    const THREE_POW_4 : u32 = 81;
+    const THREE_POW_5 : u32 = 243;
+    const THREE_POW_6 : u32 = 729;
+
+    // TNF17e's offset field, for the contrast invariant below. Kept as a local
+    // constant rather than an import so this file stays self-contained; the
+    // value is checked against tnf17.t27's own TNF_OFFSET_MAX + 1.
+    const TNF17_OFFSET_VALUES : u32 = 81;    // 3^4, and it uses all 81
+
+    // ---- Decode ----
+
+    // The unbiased exponent. Returns the exponent of a FINITE code; callers must
+    // test e8m0_is_nan first, exactly as the standard requires.
+    fn e8m0_exponent(x: u32) -> i32 {
+        return ((x as i32) - E8M0_BIAS);
+    }
+
+    fn e8m0_is_nan(x: u32) -> bool {
+        return (x == E8M0_NAN_CODE);
+    }
+
+    fn e8m0_is_finite(x: u32) -> bool {
+        return (x <= E8M0_MAX_FINITE);
+    }
+
+    // ---- Encode ----
+
+    // The inverse of e8m0_exponent, for e in [-127, +127]. Out-of-range inputs
+    // return the NaN code rather than wrapping: a scale that cannot be
+    // represented is not a scale, and silently aliasing it to a representable
+    // one is how a block of elements acquires the wrong magnitude.
+    fn e8m0_encode(e: i32) -> u32 {
+        if (e < (0 - E8M0_BIAS)) { return E8M0_NAN_CODE; }
+        if (e > E8M0_BIAS) { return E8M0_NAN_CODE; }
+        return ((e + E8M0_BIAS) as u32);
+    }
+
+    // ---- Trit accounting: the part the standard does not state ----
+
+    // Whether `n` distinct values fit in five balanced trits, six, or neither
+    // exactly. Returns the number of trits NEEDED; 0 means more than six.
+    fn trits_needed(n: u32) -> u32 {
+        if (n <= THREE_POW_3) { return 3; }
+        if (n <= THREE_POW_4) { return 4; }
+        if (n <= THREE_POW_5) { return 5; }
+        if (n <= THREE_POW_6) { return 6; }
+        return 0;
+    }
+
+    // The codes a trit count leaves unused. This is the substrate tax on the
+    // exponent field, in the same units the rest of the numeric line uses.
+    fn trit_waste(n: u32) -> u32 {
+        if (trits_needed(n) == 3) { return (THREE_POW_3 - n); }
+        if (trits_needed(n) == 4) { return (THREE_POW_4 - n); }
+        if (trits_needed(n) == 5) { return (THREE_POW_5 - n); }
+        if (trits_needed(n) == 6) { return (THREE_POW_6 - n); }
+        return 0;
+    }
+
+    // Does `n` land exactly on a power of three, i.e. pack with zero waste?
+    fn packs_exactly(n: u32) -> bool {
+        return (trit_waste(n) == 0);
+    }
+
+    // ---- Combinational port surface (T81: without this the module has no
+    // boundary and synthesises to nothing) ----
+    //
+    // Round-trip a code through decode and encode. Exercises the bias path in
+    // both directions and the NaN guard, in one function.
+    fn on_comb(x: u32) -> u32 {
+        if (e8m0_is_nan(x)) { return E8M0_NAN_CODE; }
+        return e8m0_encode(e8m0_exponent(x));
+    }
+
+    // ---- Tests ----
+
+    test one_is_code_one_two_seven
+        given e = e8m0_exponent(E8M0_ONE)
+        then e == 0
+
+    test smallest_finite_is_minus_one_two_seven
+        given e = e8m0_exponent(E8M0_MIN_FINITE)
+        then e == -127
+
+    test largest_finite_is_plus_one_two_seven
+        given e = e8m0_exponent(E8M0_MAX_FINITE)
+        then e == 127
+
+    test nan_code_is_detected
+        given r = e8m0_is_nan(E8M0_NAN_CODE)
+        then r == true
+
+    test max_finite_is_not_nan
+        given r = e8m0_is_nan(E8M0_MAX_FINITE)
+        then r == false
+
+    test nan_code_is_not_finite
+        given r = e8m0_is_finite(E8M0_NAN_CODE)
+        then r == false
+
+    test encode_zero_gives_one
+        given c = e8m0_encode(0)
+        then c == 127
+
+    test encode_minus_one_two_seven
+        given c = e8m0_encode(-127)
+        then c == 0
+
+    test encode_plus_one_two_seven
+        given c = e8m0_encode(127)
+        then c == 254
+
+    // The guard, and the reason it exists: 2^128 has no E8M0 code, and returning
+    // code 1 for it would scale a whole block by 2^-126.
+    test encode_out_of_range_is_nan
+        given c = e8m0_encode(128)
+        then c == 255
+
+    test encode_below_range_is_nan
+        given c = e8m0_encode(-128)
+        then c == 255
+
+    // ---- Trit accounting ----
+
+    test exponent_field_needs_six_trits
+        given t = trits_needed(E8M0_FINITE_CODES)
+        then t == 6
+
+    test exponent_field_wastes_four_hundred_seventy_four
+        given w = trit_waste(E8M0_FINITE_CODES)
+        then w == 474
+
+    test tnf17_offset_needs_exactly_four_trits
+        given t = trits_needed(TNF17_OFFSET_VALUES)
+        then t == 4
+
+    test tnf17_offset_wastes_nothing
+        given w = trit_waste(TNF17_OFFSET_VALUES)
+        then w == 0
+
+    // ---- Port surface ----
+
+    test comb_round_trips_one
+        given r = on_comb(E8M0_ONE)
+        then r == 127
+
+    test comb_round_trips_max_finite
+        given r = on_comb(E8M0_MAX_FINITE)
+        then r == 254
+
+    test comb_passes_nan_through
+        given r = on_comb(E8M0_NAN_CODE)
+        then r == 255
+
+    // ---- Invariants ----
+
+    // The field holds 2^8 codes, of which exactly one is reserved.
+    invariant one_code_is_reserved
+        assert E8M0_FINITE_CODES + 1 == 256
+
+    invariant bias_centres_the_range
+        assert E8M0_BIAS * 2 == (E8M0_MAX_FINITE as i32)
+
+    invariant one_sits_at_the_bias
+        assert (E8M0_ONE as i32) == E8M0_BIAS
+
+    invariant nan_is_the_code_above_max_finite
+        assert E8M0_NAN_CODE == E8M0_MAX_FINITE + 1
+
+    // THE RESULT. E8M0's exponent does not pack into trits and TNF17e's does.
+    // Stated as an assertion so the contrast is checked at compile time rather
+    // than asserted in a comment that can rot.
+    invariant e8m0_exponent_does_not_pack_into_trits
+        assert packs_exactly(E8M0_FINITE_CODES) == false
+
+    invariant tnf17_offset_packs_exactly
+        assert packs_exactly(TNF17_OFFSET_VALUES) == true
+
+    // 243 < 255 < 729 -- the reason it needs six trits and cannot use five.
+    invariant two_hundred_fifty_five_sits_between_the_powers
+        assert THREE_POW_5 < E8M0_FINITE_CODES
+
+    invariant two_hundred_fifty_five_is_below_three_to_the_six
+        assert E8M0_FINITE_CODES < THREE_POW_6
+
+    // The waste is the tax, and it is 65 percent of a six-trit field.
+    invariant six_trit_field_is_mostly_unused
+        assert trit_waste(E8M0_FINITE_CODES) > (E8M0_FINITE_CODES + 200)
+
+    // ---- Bench ----
+
+    bench e8m0_decode_encode
+        assert e8m0_exponent(E8M0_ONE) == 0
+        assert e8m0_encode(0) == E8M0_ONE
+        assert on_comb(E8M0_MAX_FINITE) == E8M0_MAX_FINITE
+}
+
+// phi^2 + 1/phi^2 = 3 | TRINITY
+

Open the lesson's spec in the player ↗

+ +

All lessons

+ +
+ + diff --git a/apps/website/public/learn/seven-backends-and-t27b/card.png b/apps/website/public/learn/seven-backends-and-t27b/card.png index 0f1383af9b..1a5dfeeb15 100644 Binary files a/apps/website/public/learn/seven-backends-and-t27b/card.png and b/apps/website/public/learn/seven-backends-and-t27b/card.png differ diff --git a/apps/website/public/learn/seven-backends-and-t27b/index.html b/apps/website/public/learn/seven-backends-and-t27b/index.html index 8db7afbe16..e32b6c5bd3 100644 --- a/apps/website/public/learn/seven-backends-and-t27b/index.html +++ b/apps/website/public/learn/seven-backends-and-t27b/index.html @@ -4,7 +4,7 @@ -Seven backends, and t27b: FPGA course, lesson 12 of 30 +Seven backends, and t27b: FPGA course, lesson 12 of 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + + + + + diff --git a/apps/website/public/play/e8m0/card.png b/apps/website/public/play/e8m0/card.png new file mode 100644 index 0000000000..6edf43c775 Binary files /dev/null and b/apps/website/public/play/e8m0/card.png differ diff --git a/apps/website/public/play/e8m0/index.html b/apps/website/public/play/e8m0/index.html new file mode 100644 index 0000000000..df1df97f73 --- /dev/null +++ b/apps/website/public/play/e8m0/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27, the OCP MX scale byte, compiled inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/js/card.png b/apps/website/public/play/e8m0/js/card.png new file mode 100644 index 0000000000..5bb5de5fc4 Binary files /dev/null and b/apps/website/public/play/e8m0/js/card.png differ diff --git a/apps/website/public/play/e8m0/js/index.html b/apps/website/public/play/e8m0/js/index.html new file mode 100644 index 0000000000..4f8350be82 --- /dev/null +++ b/apps/website/public/play/e8m0/js/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to JavaScript, in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/rust/card.png b/apps/website/public/play/e8m0/rust/card.png new file mode 100644 index 0000000000..ec3cf34a7d Binary files /dev/null and b/apps/website/public/play/e8m0/rust/card.png differ diff --git a/apps/website/public/play/e8m0/rust/index.html b/apps/website/public/play/e8m0/rust/index.html new file mode 100644 index 0000000000..3f895c1370 --- /dev/null +++ b/apps/website/public/play/e8m0/rust/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to Rust, in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/ts/card.png b/apps/website/public/play/e8m0/ts/card.png new file mode 100644 index 0000000000..aefc82e448 Binary files /dev/null and b/apps/website/public/play/e8m0/ts/card.png differ diff --git a/apps/website/public/play/e8m0/ts/index.html b/apps/website/public/play/e8m0/ts/index.html new file mode 100644 index 0000000000..c49134d13b --- /dev/null +++ b/apps/website/public/play/e8m0/ts/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to TypeScript, in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/verilog-hir/card.png b/apps/website/public/play/e8m0/verilog-hir/card.png new file mode 100644 index 0000000000..883371a761 Binary files /dev/null and b/apps/website/public/play/e8m0/verilog-hir/card.png differ diff --git a/apps/website/public/play/e8m0/verilog-hir/index.html b/apps/website/public/play/e8m0/verilog-hir/index.html new file mode 100644 index 0000000000..b5ff3774e0 --- /dev/null +++ b/apps/website/public/play/e8m0/verilog-hir/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to Verilog (HIR), in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/verilog/card.png b/apps/website/public/play/e8m0/verilog/card.png new file mode 100644 index 0000000000..37ede49315 Binary files /dev/null and b/apps/website/public/play/e8m0/verilog/card.png differ diff --git a/apps/website/public/play/e8m0/verilog/index.html b/apps/website/public/play/e8m0/verilog/index.html new file mode 100644 index 0000000000..9e71f64515 --- /dev/null +++ b/apps/website/public/play/e8m0/verilog/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to Verilog, in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/e8m0/zig/card.png b/apps/website/public/play/e8m0/zig/card.png new file mode 100644 index 0000000000..ba6b6656ea Binary files /dev/null and b/apps/website/public/play/e8m0/zig/card.png differ diff --git a/apps/website/public/play/e8m0/zig/index.html b/apps/website/public/play/e8m0/zig/index.html new file mode 100644 index 0000000000..06c7ba04d0 --- /dev/null +++ b/apps/website/public/play/e8m0/zig/index.html @@ -0,0 +1,35 @@ + + + + + + +e8m0.t27 to Zig, in a terminal inside the post · Trinity S³AI + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/public/play/hello-world/c/index.html b/apps/website/public/play/hello-world/c/index.html index 808a3899ce..d90e7a9a2d 100644 --- a/apps/website/public/play/hello-world/c/index.html +++ b/apps/website/public/play/hello-world/c/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/index.html b/apps/website/public/play/hello-world/index.html index 43ecad5693..5956c1bcbf 100644 --- a/apps/website/public/play/hello-world/index.html +++ b/apps/website/public/play/hello-world/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/js/index.html b/apps/website/public/play/hello-world/js/index.html index 69451d0e9b..a4704a1ad9 100644 --- a/apps/website/public/play/hello-world/js/index.html +++ b/apps/website/public/play/hello-world/js/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/rust/index.html b/apps/website/public/play/hello-world/rust/index.html index be6e2a48b3..ef8f36f7e0 100644 --- a/apps/website/public/play/hello-world/rust/index.html +++ b/apps/website/public/play/hello-world/rust/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/ts/index.html b/apps/website/public/play/hello-world/ts/index.html index 515e316125..93e97d7e10 100644 --- a/apps/website/public/play/hello-world/ts/index.html +++ b/apps/website/public/play/hello-world/ts/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/verilog-hir/index.html b/apps/website/public/play/hello-world/verilog-hir/index.html index 94b5f63b65..90812e7bac 100644 --- a/apps/website/public/play/hello-world/verilog-hir/index.html +++ b/apps/website/public/play/hello-world/verilog-hir/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/verilog/index.html b/apps/website/public/play/hello-world/verilog/index.html index 10c5dde7ba..f342a8d3c8 100644 --- a/apps/website/public/play/hello-world/verilog/index.html +++ b/apps/website/public/play/hello-world/verilog/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/hello-world/zig/index.html b/apps/website/public/play/hello-world/zig/index.html index 68cb65b319..3c4d46abd7 100644 --- a/apps/website/public/play/hello-world/zig/index.html +++ b/apps/website/public/play/hello-world/zig/index.html @@ -1,5 +1,5 @@ - + diff --git a/apps/website/public/play/player.t27 b/apps/website/public/play/player.t27 index efac42b7e6..1775e9e0dd 100644 --- a/apps/website/public/play/player.t27 +++ b/apps/website/public/play/player.t27 @@ -43,7 +43,7 @@ pub const NAME : str = "t27 in a post"; ; The derived files the generator rewrites (relative to apps/website): this spec's public copy, ; then the site modules below as one ES module, then per play, in PLAY_IDS order, its share page ; and one share page per backend, in BACKENDS order. -pub const GENERATED : [10]str = ["public/play/player.t27", "public/play/site.js", "public/play/hello-world/index.html", "public/play/hello-world/zig/index.html", "public/play/hello-world/c/index.html", "public/play/hello-world/rust/index.html", "public/play/hello-world/verilog/index.html", "public/play/hello-world/verilog-hir/index.html", "public/play/hello-world/js/index.html", "public/play/hello-world/ts/index.html"]; +pub const GENERATED : [18]str = ["public/play/player.t27", "public/play/site.js", "public/play/hello-world/index.html", "public/play/hello-world/zig/index.html", "public/play/hello-world/c/index.html", "public/play/hello-world/rust/index.html", "public/play/hello-world/verilog/index.html", "public/play/hello-world/verilog-hir/index.html", "public/play/hello-world/js/index.html", "public/play/hello-world/ts/index.html", "public/play/e8m0/index.html", "public/play/e8m0/zig/index.html", "public/play/e8m0/c/index.html", "public/play/e8m0/rust/index.html", "public/play/e8m0/verilog/index.html", "public/play/e8m0/verilog-hir/index.html", "public/play/e8m0/js/index.html", "public/play/e8m0/ts/index.html"]; ; --- What the player takes from the site ------------------------------------------------------- ; The spec explorer's highlighter and palette, bundled from the site's own source rather than @@ -186,13 +186,13 @@ pub const SHELL_GEN_ALIAS : str = "t27c gen-{backend}"; ; --- Plays --------------------------------------------------------------------------------------- ; One entry per play, the same position in every array. SPEC is a path under public/t27/files/; ; CAST names a recording published at public/term//. -pub const PLAY_COUNT : u8 = 1; -pub const PLAY_IDS : [1]str = ["hello-world"]; -pub const PLAY_SPECS : [1]str = ["specs/demos/hello_world.t27"]; -pub const PLAY_CASTS : [1]str = ["t27c-hello-world"]; -pub const PLAY_TITLES : [1]str = ["hello_world.t27, compiled inside the post"]; -pub const PLAY_DESCRIPTIONS : [1]str = ["The t27 compiler, as WebAssembly, running on one spec in your browser: lexer to seven backends, the spec's own tests, the emitted code, its Verilog read for synthesis, and a terminal."]; -pub const PLAY_IMAGE_ALTS : [1]str = ["The t27 player: hello_world.t27 on the left, the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal."]; +pub const PLAY_COUNT : u8 = 2; +pub const PLAY_IDS : [2]str = ["hello-world", "e8m0"]; +pub const PLAY_SPECS : [2]str = ["specs/demos/hello_world.t27", "specs/numeric/e8m0.t27"]; +pub const PLAY_CASTS : [2]str = ["t27c-hello-world", "t27c-e8m0"]; +pub const PLAY_TITLES : [2]str = ["hello_world.t27, compiled inside the post", "e8m0.t27, the OCP MX scale byte, compiled inside the post"]; +pub const PLAY_DESCRIPTIONS : [2]str = ["The t27 compiler, as WebAssembly, running on one spec in your browser: lexer to seven backends, the spec's own tests, the emitted code, its Verilog read for synthesis, and a terminal.", "The OCP MX shared scale as a t27 spec, compiled by t27c as WebAssembly in your browser: one byte, 2^(e-127), code 255 is NaN. Seven backends, the spec's own tests and its Verilog."]; +pub const PLAY_IMAGE_ALTS : [2]str = ["The t27 player: hello_world.t27 on the left, the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal.", "The t27 player: e8m0.t27, the shared scale byte of the OCP Microscaling formats, on the left; the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal."]; test the_player_is_sixteen_by_nine { assert PLAYER_WIDTH * 9 == PLAYER_HEIGHT * 16; @@ -267,11 +267,17 @@ test the_terminal_is_a_tab_and_gen_is_a_command { } test the_first_play_is_hello_world { - assert PLAY_COUNT == 1; assert PLAY_IDS[0] == "hello-world"; assert PLAY_SPECS[0] == "specs/demos/hello_world.t27"; } +test the_second_play_is_the_mx_scale_byte { + assert PLAY_COUNT == 2; + assert PLAY_IDS[1] == "e8m0"; + assert PLAY_SPECS[1] == "specs/numeric/e8m0.t27"; + assert PLAY_CASTS[1] == "t27c-e8m0"; +} + test the_terminal_reads_the_compile_stages { assert STAGES[0] == "source"; assert STAGES[5] == "backends"; diff --git a/apps/website/public/ru/learn/a-build-receipt/card.png b/apps/website/public/ru/learn/a-build-receipt/card.png index 80e876c81c..e4b4c821f1 100644 Binary files a/apps/website/public/ru/learn/a-build-receipt/card.png and b/apps/website/public/ru/learn/a-build-receipt/card.png differ diff --git a/apps/website/public/ru/learn/a-build-receipt/index.html b/apps/website/public/ru/learn/a-build-receipt/index.html index d0427b5aa6..59ed3b999b 100644 --- a/apps/website/public/ru/learn/a-build-receipt/index.html +++ b/apps/website/public/ru/learn/a-build-receipt/index.html @@ -4,7 +4,7 @@ -Сборка по пунктам: курс по FPGA, урок 23 из 30 +Сборка по пунктам: курс по FPGA, урок 23 из 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + +
t27.aiEnglish
+
+ +

Один выброс, много нулей

+

Вы узнаете

Чего стоит общий масштаб: как один крупный вес обнуляет мелкие веса своего блока, формат за форматом.

+

Общий масштаб задаёт самое большое значение в блоке, поэтому один выброс задаёт его для всех 32 весов. Веса, намного меньшие выброса, падают ниже самого мелкого шага, который может показать элемент, и записываются нулём. Эта запись нативно запускает ocp_mx.t27, нашу спеку OCP MX v1.0, на блоке из 31 мелкого веса и одного выброса и считает, сколько весов обнуляет каждый формат элемента. Затем она подкладывает ошибку, одно изменённое смещение порядка, и падает ровно один тест: спека, которая несёт свои тесты, ловит собственные ошибки.

+

Попробуйте

Найдите, какой формат элемента обнуляет больше всего мелких весов, а какие два — ни одного; затем найдите тест, который ломает подложенная ошибка.

+

Открыть интерактивный урок →

+
t27c on ocp_mx.t27 -- OCP MX v1.0, native
t27c on ocp_mx.t27 -- OCP MX v1.0, native ↗
+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway), spec at t27 1a3786657: 49 tests pass natively; one changed bias makes exactly one test fail; git restores the spec.

+ + +

Все уроки

+ +
+ + diff --git a/apps/website/public/ru/learn/one-scale-per-block/card.png b/apps/website/public/ru/learn/one-scale-per-block/card.png new file mode 100644 index 0000000000..57d7ef7c47 Binary files /dev/null and b/apps/website/public/ru/learn/one-scale-per-block/card.png differ diff --git a/apps/website/public/ru/learn/one-scale-per-block/index.html b/apps/website/public/ru/learn/one-scale-per-block/index.html new file mode 100644 index 0000000000..60e871df86 --- /dev/null +++ b/apps/website/public/ru/learn/one-scale-per-block/index.html @@ -0,0 +1,73 @@ + + + + + + +Один масштаб на блок весов: курс по FPGA, урок 31 из 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
t27.aiEnglish
+
+ +

Один масштаб на блок весов

+

Вы узнаете

Почему ИИ-форматы делят один масштаб на блок весов и как читается байт масштаба OCP MX.

+

У языковой модели миллионы весов, и хранить и пересылать полное число с плавающей точкой для каждого дорого. Форматы Microscaling (MX) консорциума Open Compute Project хранят каждый вес в 4, 6 или 8 битах и дают каждому блоку из 32 весов один общий масштаб. Этот масштаб — один байт, E8M0: без знака и без мантиссы, только порядок, так что код e означает 2 в степени e минус 127, а код 255 означает NaN. Плеер компилирует e8m0.t27, спеку этого байта, прямо в вашем браузере и запускает её тесты. Проверку, которую браузер выполнить не может, он пропускает и объясняет почему.

+

Попробуйте

Запустите тесты и посчитайте пропуски; затем откройте Verilog и найдите функцию, которая проверяет код 255.

+

Открыть интерактивный урок →

+
e8m0.t27, the OCP MX scale byte, compiled inside the post
e8m0.t27, the OCP MX scale byte, compiled inside the post ↗
+

The OCP MX shared scale as a t27 spec, compiled by t27c as WebAssembly in your browser: one byte, 2^(e-127), code 255 is NaN. Seven backends, the spec's own tests and its Verilog.

+ + +

Все уроки

+ +
+ + diff --git a/apps/website/public/ru/learn/pins-and-package/card.png b/apps/website/public/ru/learn/pins-and-package/card.png index a71c91f2f1..a3fce20098 100644 Binary files a/apps/website/public/ru/learn/pins-and-package/card.png and b/apps/website/public/ru/learn/pins-and-package/card.png differ diff --git a/apps/website/public/ru/learn/pins-and-package/index.html b/apps/website/public/ru/learn/pins-and-package/index.html index 2104595a94..bdce9b431c 100644 --- a/apps/website/public/ru/learn/pins-and-package/index.html +++ b/apps/website/public/ru/learn/pins-and-package/index.html @@ -4,7 +4,7 @@ -Выводы, шарики и корпус: курс по FPGA, урок 3 из 30 +Выводы, шарики и корпус: курс по FPGA, урок 3 из 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + +
t27.aiEnglish
+
+ +

Байт масштаба на настоящей машине

+

Вы узнаете

Что нативный компилятор проверяет в той же спеке, включая то, что браузер пропустил.

+

Плеер в прошлом уроке не смог выполнить все проверки в e8m0.t27 и прямо об этом сказал. Эта запись запускает нативный t27c и Zig на настоящей машине на той же спеке: её константы, отчёт о тестах, тесты Zig и Verilog для проверки на NaN. Часть проверок — инварианты, которые компилятор доказывает во время компиляции, так что компиляция и есть проверка. Каждый байт в записи напечатала команда; инсценирован только набор текста.

+

Попробуйте

Найдите, сколько инвариантов отчёт доказал во время компиляции, и сравните число пройденных тестов с числом у плеера.

+

Открыть интерактивный урок →

+
t27c on e8m0.t27 -- the MX shared scale byte, native
t27c on e8m0.t27 -- the MX shared scale byte, native ↗
+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway): the E8M0 scale byte of OCP MX, 18 tests and 9 comptime invariants pass, and the Verilog backend's NaN check.

+

specs/numeric/e8m0.t27

// SPDX-License-Identifier: Apache-2.0
+// t27/specs/numeric/e8m0.t27
+// E8M0 -- the OCP Microscaling shared scale, and the trit count it does not have.
+//
+// WHY THIS EXISTS. `specs/numeric/formats_catalog.t27` is the numeric SSOT and it
+// names `e8m0` FIVE times as the storage of another format --
+//
+//     mxfp8   storage=u8_plus_shared_e8m0
+//     mxfp6   storage=u8_packed_plus_e8m0
+//     mxfp4   storage=u8_packed_plus_e8m0
+//     mxgf6   storage=u8_packed_plus_e8m0
+//     mxgf4   storage=u8_packed_plus_e8m0
+//
+// -- and `grep "id=e8m0 "` over the same file returns ZERO. The SSOT depends on a
+// component it never defines. This file defines it. Measured 2026-08-16.
+//
+// LAYOUT.  E8M0 = [ exponent(8) ].  No sign. No mantissa. No implicit one.
+//
+//     value = 2^(E - 127),   E in [0, 254]
+//     E = 255                is NaN
+//
+// Source: OCP Microscaling Formats (MX) Specification v1.0, Rouhani et al. 2023,
+// arXiv:2310.10537 -- the citation the catalogue already carries for every one of
+// the five dependants.
+//
+// THE RESULT THIS MIGRATION PRODUCED, which is why it is worth a spec and not a
+// constant. E8M0's exponent takes 255 distinct finite values, e in [-127, +127].
+// Five balanced trits span 3^5 = 243 and six span 3^6 = 729, so
+//
+//     243 < 255 < 729
+//
+// and E8M0's exponent field is NOT a whole number of trits. It needs six and
+// uses 255 of 729 -- 35 percent. Contrast `specs/numeric/tnf17.t27`, whose
+// offset field holds exactly 81 = 3^4 values and uses 81 of 81. That contrast is
+// stated below as a comptime invariant rather than as prose, because it is the
+// only thing this format tells us that its own standard does not.
+//
+// WHAT THIS IS NOT. E8M0 is a SHARED SCALE, applied once per block of elements,
+// not a weight code. Under the golden sieve (specs/numeric/golden_sieve.t27) it
+// is not a candidate at all: S1 asks |A| = 3^k and 255 is not a power of three.
+// It is also, for the same reason it is cheap, exempt from the sieve's concerns
+// -- a pure power of two applied to a whole block is one shift, one lane, no
+// DSP, and it never enters a neuron's truth table.
+//
+// phi^2 + phi^-2 = 3 | TRINITY
+
+module E8M0 {
+
+    // ---- Field geometry, from the OCP MX v1.0 specification ----
+
+    const E8M0_BITS       : u32 = 8;
+    const E8M0_BIAS       : i32 = 127;   // code 127 == 2^0
+    const E8M0_NAN_CODE   : u32 = 255;   // the one reserved code
+    const E8M0_MAX_FINITE : u32 = 254;   // largest finite code, 2^127
+    const E8M0_MIN_FINITE : u32 = 0;     // smallest finite code, 2^-127
+    const E8M0_ONE        : u32 = 127;   // the code whose value is 1.0
+
+    // Distinct finite values the field can hold: 0..254 inclusive.
+    const E8M0_FINITE_CODES : u32 = 255;
+
+    // Trit spans, written out because the numeric core has no exponentiation.
+    //
+    // W781 DEFECT, caught by the comptime invariant that is the reason this file
+    // has invariants at all: the first version of this table started at 3^5, so
+    // `trits_needed(81)` answered 5 and `packs_exactly(81)` came back FALSE for a
+    // number that is exactly 3^4. The table must reach DOWN to every power a
+    // caller might land on, not only up to the one the subject format needs.
+    const THREE_POW_3 : u32 = 27;
+    const THREE_POW_4 : u32 = 81;
+    const THREE_POW_5 : u32 = 243;
+    const THREE_POW_6 : u32 = 729;
+
+    // TNF17e's offset field, for the contrast invariant below. Kept as a local
+    // constant rather than an import so this file stays self-contained; the
+    // value is checked against tnf17.t27's own TNF_OFFSET_MAX + 1.
+    const TNF17_OFFSET_VALUES : u32 = 81;    // 3^4, and it uses all 81
+
+    // ---- Decode ----
+
+    // The unbiased exponent. Returns the exponent of a FINITE code; callers must
+    // test e8m0_is_nan first, exactly as the standard requires.
+    fn e8m0_exponent(x: u32) -> i32 {
+        return ((x as i32) - E8M0_BIAS);
+    }
+
+    fn e8m0_is_nan(x: u32) -> bool {
+        return (x == E8M0_NAN_CODE);
+    }
+
+    fn e8m0_is_finite(x: u32) -> bool {
+        return (x <= E8M0_MAX_FINITE);
+    }
+
+    // ---- Encode ----
+
+    // The inverse of e8m0_exponent, for e in [-127, +127]. Out-of-range inputs
+    // return the NaN code rather than wrapping: a scale that cannot be
+    // represented is not a scale, and silently aliasing it to a representable
+    // one is how a block of elements acquires the wrong magnitude.
+    fn e8m0_encode(e: i32) -> u32 {
+        if (e < (0 - E8M0_BIAS)) { return E8M0_NAN_CODE; }
+        if (e > E8M0_BIAS) { return E8M0_NAN_CODE; }
+        return ((e + E8M0_BIAS) as u32);
+    }
+
+    // ---- Trit accounting: the part the standard does not state ----
+
+    // Whether `n` distinct values fit in five balanced trits, six, or neither
+    // exactly. Returns the number of trits NEEDED; 0 means more than six.
+    fn trits_needed(n: u32) -> u32 {
+        if (n <= THREE_POW_3) { return 3; }
+        if (n <= THREE_POW_4) { return 4; }
+        if (n <= THREE_POW_5) { return 5; }
+        if (n <= THREE_POW_6) { return 6; }
+        return 0;
+    }
+
+    // The codes a trit count leaves unused. This is the substrate tax on the
+    // exponent field, in the same units the rest of the numeric line uses.
+    fn trit_waste(n: u32) -> u32 {
+        if (trits_needed(n) == 3) { return (THREE_POW_3 - n); }
+        if (trits_needed(n) == 4) { return (THREE_POW_4 - n); }
+        if (trits_needed(n) == 5) { return (THREE_POW_5 - n); }
+        if (trits_needed(n) == 6) { return (THREE_POW_6 - n); }
+        return 0;
+    }
+
+    // Does `n` land exactly on a power of three, i.e. pack with zero waste?
+    fn packs_exactly(n: u32) -> bool {
+        return (trit_waste(n) == 0);
+    }
+
+    // ---- Combinational port surface (T81: without this the module has no
+    // boundary and synthesises to nothing) ----
+    //
+    // Round-trip a code through decode and encode. Exercises the bias path in
+    // both directions and the NaN guard, in one function.
+    fn on_comb(x: u32) -> u32 {
+        if (e8m0_is_nan(x)) { return E8M0_NAN_CODE; }
+        return e8m0_encode(e8m0_exponent(x));
+    }
+
+    // ---- Tests ----
+
+    test one_is_code_one_two_seven
+        given e = e8m0_exponent(E8M0_ONE)
+        then e == 0
+
+    test smallest_finite_is_minus_one_two_seven
+        given e = e8m0_exponent(E8M0_MIN_FINITE)
+        then e == -127
+
+    test largest_finite_is_plus_one_two_seven
+        given e = e8m0_exponent(E8M0_MAX_FINITE)
+        then e == 127
+
+    test nan_code_is_detected
+        given r = e8m0_is_nan(E8M0_NAN_CODE)
+        then r == true
+
+    test max_finite_is_not_nan
+        given r = e8m0_is_nan(E8M0_MAX_FINITE)
+        then r == false
+
+    test nan_code_is_not_finite
+        given r = e8m0_is_finite(E8M0_NAN_CODE)
+        then r == false
+
+    test encode_zero_gives_one
+        given c = e8m0_encode(0)
+        then c == 127
+
+    test encode_minus_one_two_seven
+        given c = e8m0_encode(-127)
+        then c == 0
+
+    test encode_plus_one_two_seven
+        given c = e8m0_encode(127)
+        then c == 254
+
+    // The guard, and the reason it exists: 2^128 has no E8M0 code, and returning
+    // code 1 for it would scale a whole block by 2^-126.
+    test encode_out_of_range_is_nan
+        given c = e8m0_encode(128)
+        then c == 255
+
+    test encode_below_range_is_nan
+        given c = e8m0_encode(-128)
+        then c == 255
+
+    // ---- Trit accounting ----
+
+    test exponent_field_needs_six_trits
+        given t = trits_needed(E8M0_FINITE_CODES)
+        then t == 6
+
+    test exponent_field_wastes_four_hundred_seventy_four
+        given w = trit_waste(E8M0_FINITE_CODES)
+        then w == 474
+
+    test tnf17_offset_needs_exactly_four_trits
+        given t = trits_needed(TNF17_OFFSET_VALUES)
+        then t == 4
+
+    test tnf17_offset_wastes_nothing
+        given w = trit_waste(TNF17_OFFSET_VALUES)
+        then w == 0
+
+    // ---- Port surface ----
+
+    test comb_round_trips_one
+        given r = on_comb(E8M0_ONE)
+        then r == 127
+
+    test comb_round_trips_max_finite
+        given r = on_comb(E8M0_MAX_FINITE)
+        then r == 254
+
+    test comb_passes_nan_through
+        given r = on_comb(E8M0_NAN_CODE)
+        then r == 255
+
+    // ---- Invariants ----
+
+    // The field holds 2^8 codes, of which exactly one is reserved.
+    invariant one_code_is_reserved
+        assert E8M0_FINITE_CODES + 1 == 256
+
+    invariant bias_centres_the_range
+        assert E8M0_BIAS * 2 == (E8M0_MAX_FINITE as i32)
+
+    invariant one_sits_at_the_bias
+        assert (E8M0_ONE as i32) == E8M0_BIAS
+
+    invariant nan_is_the_code_above_max_finite
+        assert E8M0_NAN_CODE == E8M0_MAX_FINITE + 1
+
+    // THE RESULT. E8M0's exponent does not pack into trits and TNF17e's does.
+    // Stated as an assertion so the contrast is checked at compile time rather
+    // than asserted in a comment that can rot.
+    invariant e8m0_exponent_does_not_pack_into_trits
+        assert packs_exactly(E8M0_FINITE_CODES) == false
+
+    invariant tnf17_offset_packs_exactly
+        assert packs_exactly(TNF17_OFFSET_VALUES) == true
+
+    // 243 < 255 < 729 -- the reason it needs six trits and cannot use five.
+    invariant two_hundred_fifty_five_sits_between_the_powers
+        assert THREE_POW_5 < E8M0_FINITE_CODES
+
+    invariant two_hundred_fifty_five_is_below_three_to_the_six
+        assert E8M0_FINITE_CODES < THREE_POW_6
+
+    // The waste is the tax, and it is 65 percent of a six-trit field.
+    invariant six_trit_field_is_mostly_unused
+        assert trit_waste(E8M0_FINITE_CODES) > (E8M0_FINITE_CODES + 200)
+
+    // ---- Bench ----
+
+    bench e8m0_decode_encode
+        assert e8m0_exponent(E8M0_ONE) == 0
+        assert e8m0_encode(0) == E8M0_ONE
+        assert on_comb(E8M0_MAX_FINITE) == E8M0_MAX_FINITE
+}
+
+// phi^2 + 1/phi^2 = 3 | TRINITY
+

Открыть спеку урока в плеере ↗

+ +

Все уроки

+ +
+ + diff --git a/apps/website/public/ru/learn/seven-backends-and-t27b/card.png b/apps/website/public/ru/learn/seven-backends-and-t27b/card.png index f0c32b25f6..a4d814d2d3 100644 Binary files a/apps/website/public/ru/learn/seven-backends-and-t27b/card.png and b/apps/website/public/ru/learn/seven-backends-and-t27b/card.png differ diff --git a/apps/website/public/ru/learn/seven-backends-and-t27b/index.html b/apps/website/public/ru/learn/seven-backends-and-t27b/index.html index 48f9a1e257..bccbb50f2c 100644 --- a/apps/website/public/ru/learn/seven-backends-and-t27b/index.html +++ b/apps/website/public/ru/learn/seven-backends-and-t27b/index.html @@ -4,7 +4,7 @@ -Семь бэкендов и t27b: курс по FPGA, урок 12 из 30 +Семь бэкендов и t27b: курс по FPGA, урок 12 из 33 @@ -16,18 +16,18 @@ - + - + - - + + - + + + +
+ +

t27c on e8m0.t27 -- the MX shared scale byte, native

+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway): the E8M0 scale byte of OCP MX, 18 tests and 9 comptime invariants pass, and the Verilog backend's NaN check.

+
+
recorded 2026-10-06 14:11 UTCreal 19.9 sshown 19.9 sexit codes 0 0 0 0 0
+
$ git log -1 --format='%h %ad %s' --date=short -- specs/numeric/e8m0.t27 | cut -c1-100 +$ sed -n 51,56p specs/numeric/e8m0.t27 +$ t27c test-report specs/numeric/e8m0.t27 --specs-dir specs | sed -n '1,7p;$p' +$ t27c gen specs/numeric/e8m0.t27 > e8m0.zig && zig test e8m0.zig +$ t27c gen-verilog specs/numeric/e8m0.t27 | sed -n 69,78p
+

Staged: the prompt and the typing. Real: every byte the commands printed, at the time they printed it. Any silence longer than 2 s is shown for 2 s, and the title bar says so while it happens. Edited: home directory shown as ~.

+ +
+ + + diff --git a/apps/website/public/term/t27c-e8m0/meta.json b/apps/website/public/term/t27c-e8m0/meta.json new file mode 100644 index 0000000000..37e4a3f4a2 --- /dev/null +++ b/apps/website/public/term/t27c-e8m0/meta.json @@ -0,0 +1,29 @@ +{ + "id": "t27c-e8m0", + "title": "t27c on e8m0.t27 -- the MX shared scale byte, native", + "desc": "t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway): the E8M0 scale byte of OCP MX, 18 tests and 9 comptime invariants pass, and the Verilog backend's NaN check.", + "url": "https://t27.ai/term/t27c-e8m0/", + "gif": "https://t27.ai/term/t27c-e8m0/session.gif", + "post": null, + "recorded": "2026-10-06 14:11 UTC", + "real_s": 19.9, + "shown_s": 19.9, + "commands": [ + "git log -1 --format='%h %ad %s' --date=short -- specs/numeric/e8m0.t27 | cut -c1-100", + "sed -n 51,56p specs/numeric/e8m0.t27", + "t27c test-report specs/numeric/e8m0.t27 --specs-dir specs | sed -n '1,7p;$p'", + "t27c gen specs/numeric/e8m0.t27 > e8m0.zig && zig test e8m0.zig", + "t27c gen-verilog specs/numeric/e8m0.t27 | sed -n 69,78p" + ], + "exit_codes": [ + "0", + "0", + "0", + "0", + "0" + ], + "redacted": [ + "home directory shown as ~" + ], + "published": "2026-10-06" +} diff --git a/apps/website/public/term/t27c-e8m0/session.cast b/apps/website/public/term/t27c-e8m0/session.cast new file mode 100644 index 0000000000..aaeaaec1d3 --- /dev/null +++ b/apps/website/public/term/t27c-e8m0/session.cast @@ -0,0 +1,337 @@ +{"version": 2, "width": 104, "height": 30, "timestamp": 1791295901, "title": "t27c on e8m0.t27 -- the MX shared scale byte, native", "commands": ["git log -1 --format='%h %ad %s' --date=short -- specs/numeric/e8m0.t27 | cut -c1-100", "sed -n 51,56p specs/numeric/e8m0.t27", "t27c test-report specs/numeric/e8m0.t27 --specs-dir specs | sed -n '1,7p;$p'", "t27c gen specs/numeric/e8m0.t27 > e8m0.zig && zig test e8m0.zig", "t27c gen-verilog specs/numeric/e8m0.t27 | sed -n 69,78p"], "redacted": ["home directory shown as ~"]} +[0.0, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[0.4001, "o", "g"] +[0.4352, "o", "i"] +[0.4703, "o", "t"] +[0.5054, "o", " "] +[0.5405, "o", "l"] +[0.5756, "o", "o"] +[0.6107, "o", "g"] +[0.6458, "o", " "] +[0.681, "o", "-"] +[0.716, "o", "1"] +[0.7511, "o", " "] +[0.7862, "o", "-"] +[0.8213, "o", "-"] +[0.8564, "o", "f"] +[0.8915, "o", "o"] +[0.9265, "o", "r"] +[0.9616, "o", "m"] +[0.9967, "o", "a"] +[1.0318, "o", "t"] +[1.0669, "o", "="] +[1.102, "o", "'"] +[1.1371, "o", "%"] +[1.1722, "o", "h"] +[1.2073, "o", " "] +[1.2424, "o", "%"] +[1.2775, "o", "a"] +[1.3129, "o", "d"] +[1.348, "o", " "] +[1.3831, "o", "%"] +[1.4182, "o", "s"] +[1.4533, "o", "'"] +[1.4884, "o", " "] +[1.5235, "o", "-"] +[1.5586, "o", "-"] +[1.5937, "o", "d"] +[1.6288, "o", "a"] +[1.6639, "o", "t"] +[1.699, "o", "e"] +[1.7341, "o", "="] +[1.7691, "o", "s"] +[1.8042, "o", "h"] +[1.8393, "o", "o"] +[1.8744, "o", "r"] +[1.9095, "o", "t"] +[1.9446, "o", " "] +[1.9797, "o", "-"] +[2.0148, "o", "-"] +[2.0499, "o", " "] +[2.085, "o", "s"] +[2.1201, "o", "p"] +[2.1551, "o", "e"] +[2.1903, "o", "c"] +[2.2254, "o", "s"] +[2.2604, "o", "/"] +[2.2955, "o", "n"] +[2.3306, "o", "u"] +[2.3657, "o", "m"] +[2.4008, "o", "e"] +[2.4359, "o", "r"] +[2.471, "o", "i"] +[2.5061, "o", "c"] +[2.5412, "o", "/"] +[2.5762, "o", "e"] +[2.6113, "o", "8"] +[2.6464, "o", "m"] +[2.6815, "o", "0"] +[2.7166, "o", "."] +[2.7517, "o", "t"] +[2.7868, "o", "2"] +[2.8219, "o", "7"] +[2.857, "o", " "] +[2.8921, "o", "|"] +[2.9272, "o", " "] +[2.9622, "o", "c"] +[2.9973, "o", "u"] +[3.0324, "o", "t"] +[3.0694, "o", " "] +[3.1045, "o", "-"] +[3.1396, "o", "c"] +[3.1747, "o", "1"] +[3.2098, "o", "-"] +[3.2449, "o", "1"] +[3.28, "o", "0"] +[3.315, "o", "0"] +[3.6502, "o", "\r\n"] +[3.6807, "o", "d4417fe72 2026-08-15 feat(numeric): W781 -- T436-T438, e8m0.t27, the first migration that finished (\r\n"] +[3.6818, "x", "0"] +[4.6819, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[5.082, "o", "s"] +[5.1171, "o", "e"] +[5.1522, "o", "d"] +[5.1873, "o", " "] +[5.2224, "o", "-"] +[5.2575, "o", "n"] +[5.2926, "o", " "] +[5.3277, "o", "5"] +[5.3628, "o", "1"] +[5.3979, "o", ","] +[5.433, "o", "5"] +[5.468, "o", "6"] +[5.5031, "o", "p"] +[5.5382, "o", " "] +[5.5733, "o", "s"] +[5.6084, "o", "p"] +[5.6434, "o", "e"] +[5.6785, "o", "c"] +[5.7136, "o", "s"] +[5.7487, "o", "/"] +[5.7838, "o", "n"] +[5.8189, "o", "u"] +[5.854, "o", "m"] +[5.8891, "o", "e"] +[5.9242, "o", "r"] +[5.9593, "o", "i"] +[5.9943, "o", "c"] +[6.0294, "o", "/"] +[6.0645, "o", "e"] +[6.0996, "o", "8"] +[6.1347, "o", "m"] +[6.1698, "o", "0"] +[6.2049, "o", "."] +[6.24, "o", "t"] +[6.2751, "o", "2"] +[6.3102, "o", "7"] +[6.6454, "o", "\r\n"] +[6.65, "o", " const E8M0_BITS : u32 = 8;\r\n const E8M0_BIAS : i32 = 127; // code 127 == 2^0\r\n const E8M0_NAN_CODE : u32 = 255; // the one reserved code\r\n const E8M0_MAX_FINITE : u32 = 254; // largest finite code, 2^127\r\n const E8M0_MIN_FINITE : u32 = 0; // smallest finite code, 2^-127\r\n const E8M0_ONE : u32 = 127; // the code whose value is 1.0\r\n"] +[6.6501, "x", "0"] +[7.6501, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[8.0503, "o", "t"] +[8.0854, "o", "2"] +[8.1205, "o", "7"] +[8.1556, "o", "c"] +[8.1907, "o", " "] +[8.2258, "o", "t"] +[8.2608, "o", "e"] +[8.2961, "o", "s"] +[8.3312, "o", "t"] +[8.3663, "o", "-"] +[8.4014, "o", "r"] +[8.4365, "o", "e"] +[8.4716, "o", "p"] +[8.5067, "o", "o"] +[8.5418, "o", "r"] +[8.5769, "o", "t"] +[8.612, "o", " "] +[8.6471, "o", "s"] +[8.6822, "o", "p"] +[8.7173, "o", "e"] +[8.7524, "o", "c"] +[8.7875, "o", "s"] +[8.8225, "o", "/"] +[8.8576, "o", "n"] +[8.8927, "o", "u"] +[8.9278, "o", "m"] +[8.9629, "o", "e"] +[8.998, "o", "r"] +[9.0331, "o", "i"] +[9.0681, "o", "c"] +[9.1032, "o", "/"] +[9.1383, "o", "e"] +[9.1734, "o", "8"] +[9.2085, "o", "m"] +[9.2436, "o", "0"] +[9.2787, "o", "."] +[9.3138, "o", "t"] +[9.3489, "o", "2"] +[9.384, "o", "7"] +[9.4191, "o", " "] +[9.4542, "o", "-"] +[9.4893, "o", "-"] +[9.5244, "o", "s"] +[9.5595, "o", "p"] +[9.5946, "o", "e"] +[9.6297, "o", "c"] +[9.6648, "o", "s"] +[9.6999, "o", "-"] +[9.7349, "o", "d"] +[9.7702, "o", "i"] +[9.8053, "o", "r"] +[9.8404, "o", " "] +[9.8755, "o", "s"] +[9.9106, "o", "p"] +[9.9456, "o", "e"] +[9.9807, "o", "c"] +[10.0158, "o", "s"] +[10.0509, "o", " "] +[10.086, "o", "|"] +[10.1211, "o", " "] +[10.1562, "o", "s"] +[10.1913, "o", "e"] +[10.2264, "o", "d"] +[10.2614, "o", " "] +[10.2965, "o", "-"] +[10.3316, "o", "n"] +[10.3667, "o", " "] +[10.4018, "o", "'"] +[10.4369, "o", "1"] +[10.472, "o", ","] +[10.5071, "o", "7"] +[10.5422, "o", "p"] +[10.5773, "o", ";"] +[10.6124, "o", "$"] +[10.6475, "o", "p"] +[10.6825, "o", "'"] +[11.0177, "o", "\r\n"] +[11.3267, "o", "--- test report: specs/numeric/e8m0.t27 ---\r\n\r\n tests 18\r\n pass 18\r\n FAIL 0\r\n invariants 9 proved -- comptime, so compiling IS the check\r\n rate 100.0%\r\n"] +[11.3271, "o", " vacuous passes 0 of 18 (passed with 0 runtime asserts executed)\r\n"] +[11.3275, "x", "0"] +[12.3276, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[12.7277, "o", "t"] +[12.7628, "o", "2"] +[12.7979, "o", "7"] +[12.833, "o", "c"] +[12.8681, "o", " "] +[12.9032, "o", "g"] +[12.9383, "o", "e"] +[12.9733, "o", "n"] +[13.0084, "o", " "] +[13.0435, "o", "s"] +[13.0786, "o", "p"] +[13.1137, "o", "e"] +[13.1488, "o", "c"] +[13.1839, "o", "s"] +[13.219, "o", "/"] +[13.254, "o", "n"] +[13.2891, "o", "u"] +[13.3242, "o", "m"] +[13.3593, "o", "e"] +[13.3944, "o", "r"] +[13.4295, "o", "i"] +[13.4645, "o", "c"] +[13.4996, "o", "/"] +[13.5347, "o", "e"] +[13.5698, "o", "8"] +[13.6049, "o", "m"] +[13.64, "o", "0"] +[13.6751, "o", "."] +[13.7102, "o", "t"] +[13.7453, "o", "2"] +[13.7803, "o", "7"] +[13.817, "o", " "] +[13.8521, "o", ">"] +[13.8872, "o", " "] +[13.9223, "o", "e"] +[13.9574, "o", "8"] +[13.9925, "o", "m"] +[14.0275, "o", "0"] +[14.0627, "o", "."] +[14.0978, "o", "z"] +[14.1329, "o", "i"] +[14.1679, "o", "g"] +[14.203, "o", " "] +[14.2381, "o", "&"] +[14.2732, "o", "&"] +[14.3083, "o", " "] +[14.3434, "o", "z"] +[14.3785, "o", "i"] +[14.4136, "o", "g"] +[14.4487, "o", " "] +[14.4837, "o", "t"] +[14.5188, "o", "e"] +[14.5539, "o", "s"] +[14.589, "o", "t"] +[14.6241, "o", " "] +[14.6592, "o", "e"] +[14.6943, "o", "8"] +[14.7294, "o", "m"] +[14.7645, "o", "0"] +[14.7996, "o", "."] +[14.8379, "o", "z"] +[14.873, "o", "i"] +[14.9081, "o", "g"] +[15.2435, "o", "\r\n"] +[15.2644, "o", "All 18 tests passed.\r\n"] +[15.2659, "x", "0"] +[16.266, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[16.6661, "o", "t"] +[16.7012, "o", "2"] +[16.7363, "o", "7"] +[16.7714, "o", "c"] +[16.8065, "o", " "] +[16.8416, "o", "g"] +[16.8767, "o", "e"] +[16.9118, "o", "n"] +[16.9468, "o", "-"] +[16.9819, "o", "v"] +[17.017, "o", "e"] +[17.0521, "o", "r"] +[17.0872, "o", "i"] +[17.1223, "o", "l"] +[17.1574, "o", "o"] +[17.1924, "o", "g"] +[17.2275, "o", " "] +[17.2626, "o", "s"] +[17.2977, "o", "p"] +[17.3328, "o", "e"] +[17.3679, "o", "c"] +[17.403, "o", "s"] +[17.4381, "o", "/"] +[17.4732, "o", "n"] +[17.5082, "o", "u"] +[17.5433, "o", "m"] +[17.5784, "o", "e"] +[17.6135, "o", "r"] +[17.6486, "o", "i"] +[17.6836, "o", "c"] +[17.7187, "o", "/"] +[17.7538, "o", "e"] +[17.7889, "o", "8"] +[17.824, "o", "m"] +[17.8591, "o", "0"] +[17.8941, "o", "."] +[17.9292, "o", "t"] +[17.9643, "o", "2"] +[17.9994, "o", "7"] +[18.0344, "o", " "] +[18.0695, "o", "|"] +[18.1046, "o", " "] +[18.1397, "o", "s"] +[18.1748, "o", "e"] +[18.2099, "o", "d"] +[18.245, "o", " "] +[18.2801, "o", "-"] +[18.3152, "o", "n"] +[18.3503, "o", " "] +[18.3854, "o", "6"] +[18.4205, "o", "9"] +[18.4556, "o", ","] +[18.4907, "o", "7"] +[18.5257, "o", "8"] +[18.5608, "o", "p"] +[18.896, "o", "\r\n"] +[18.9026, "o", " // function: e8m0_is_nan\r\n function e8m0_is_nan; // -> bool\r\n input [31:0] x;\r\n begin : e8m0_is_nan_body\r\n reg __t27_ret;\r\n __t27_ret = 1'b0;\r\n e8m0_is_nan = (x == E8M0_NAN_CODE);\r\n __t27_ret = 1'b1;\r\n end\r\n endfunction\r\n"] +[18.9033, "x", "0"] +[19.9034, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] diff --git a/apps/website/public/term/t27c-e8m0/session.gif b/apps/website/public/term/t27c-e8m0/session.gif new file mode 100644 index 0000000000..ef518dcce7 Binary files /dev/null and b/apps/website/public/term/t27c-e8m0/session.gif differ diff --git a/apps/website/public/term/t27c-ocp-mx/card.png b/apps/website/public/term/t27c-ocp-mx/card.png new file mode 100644 index 0000000000..1d692b6a88 Binary files /dev/null and b/apps/website/public/term/t27c-ocp-mx/card.png differ diff --git a/apps/website/public/term/t27c-ocp-mx/index.html b/apps/website/public/term/t27c-ocp-mx/index.html new file mode 100644 index 0000000000..8da9b16cca --- /dev/null +++ b/apps/website/public/term/t27c-ocp-mx/index.html @@ -0,0 +1,101 @@ + + + + + +t27c on ocp_mx.t27 -- OCP MX v1.0, native · Trinity S³AI + + + + + + + + + + + + + + + + + + + + +
+ +

t27c on ocp_mx.t27 -- OCP MX v1.0, native

+

t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway), spec at t27 1a3786657: 49 tests pass natively; one changed bias makes exactly one test fail; git restores the spec.

+
+
recorded 2026-10-06 14:11 UTCreal 32.7 sshown 32.7 sexit codes 0 0 0 0 0 0 0 0
+
$ git log -1 --format='%h %s' | cut -c1-100 +$ t27c --version && zig version +$ sed -n 657,664p specs/numeric/ocp_mx.t27 +$ t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n '1,7p;$p' +$ t27c gen specs/numeric/ocp_mx.t27 > ocp_mx.zig && zig test ocp_mx.zig +$ sed -i 's/= \[1, 1, 3, 7, 15, 0\]/= [1, 1, 3, 7, 16, 0]/' specs/numeric/ocp_mx.t27 && git diff -U0 | tail -n 2 +$ t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n 1,8p +$ git checkout specs/numeric/ocp_mx.t27 && sha256sum specs/numeric/ocp_mx.t27
+

Staged: the prompt and the typing. Real: every byte the commands printed, at the time they printed it. Any silence longer than 2 s is shown for 2 s, and the title bar says so while it happens. Edited: home directory shown as ~.

+ +
+ + + diff --git a/apps/website/public/term/t27c-ocp-mx/meta.json b/apps/website/public/term/t27c-ocp-mx/meta.json new file mode 100644 index 0000000000..80ec80b7d7 --- /dev/null +++ b/apps/website/public/term/t27c-ocp-mx/meta.json @@ -0,0 +1,35 @@ +{ + "id": "t27c-ocp-mx", + "title": "t27c on ocp_mx.t27 -- OCP MX v1.0, native", + "desc": "t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway), spec at t27 1a3786657: 49 tests pass natively; one changed bias makes exactly one test fail; git restores the spec.", + "url": "https://t27.ai/term/t27c-ocp-mx/", + "gif": "https://t27.ai/term/t27c-ocp-mx/session.gif", + "post": null, + "recorded": "2026-10-06 14:11 UTC", + "real_s": 32.7, + "shown_s": 32.7, + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sed -n 657,664p specs/numeric/ocp_mx.t27", + "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n '1,7p;$p'", + "t27c gen specs/numeric/ocp_mx.t27 > ocp_mx.zig && zig test ocp_mx.zig", + "sed -i 's/= \\[1, 1, 3, 7, 15, 0\\]/= [1, 1, 3, 7, 16, 0]/' specs/numeric/ocp_mx.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n 1,8p", + "git checkout specs/numeric/ocp_mx.t27 && sha256sum specs/numeric/ocp_mx.t27" + ], + "exit_codes": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ], + "redacted": [ + "home directory shown as ~" + ], + "published": "2026-10-06" +} diff --git a/apps/website/public/term/t27c-ocp-mx/session.cast b/apps/website/public/term/t27c-ocp-mx/session.cast new file mode 100644 index 0000000000..6367e20e4c --- /dev/null +++ b/apps/website/public/term/t27c-ocp-mx/session.cast @@ -0,0 +1,552 @@ +{"version": 2, "width": 104, "height": 30, "timestamp": 1791295881, "title": "t27c on ocp_mx.t27 -- OCP MX v1.0, native", "commands": ["git log -1 --format='%h %s' | cut -c1-100", "t27c --version && zig version", "sed -n 657,664p specs/numeric/ocp_mx.t27", "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n '1,7p;$p'", "t27c gen specs/numeric/ocp_mx.t27 > ocp_mx.zig && zig test ocp_mx.zig", "sed -i 's/= \\[1, 1, 3, 7, 15, 0\\]/= [1, 1, 3, 7, 16, 0]/' specs/numeric/ocp_mx.t27 && git diff -U0 | tail -n 2", "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n 1,8p", "git checkout specs/numeric/ocp_mx.t27 && sha256sum specs/numeric/ocp_mx.t27"], "redacted": ["home directory shown as ~"]} +[0.0, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[0.4001, "o", "g"] +[0.4352, "o", "i"] +[0.4703, "o", "t"] +[0.5054, "o", " "] +[0.5405, "o", "l"] +[0.5756, "o", "o"] +[0.6107, "o", "g"] +[0.6458, "o", " "] +[0.6809, "o", "-"] +[0.716, "o", "1"] +[0.7511, "o", " "] +[0.7861, "o", "-"] +[0.8212, "o", "-"] +[0.8563, "o", "f"] +[0.8914, "o", "o"] +[0.9265, "o", "r"] +[0.9616, "o", "m"] +[0.9967, "o", "a"] +[1.0318, "o", "t"] +[1.0669, "o", "="] +[1.102, "o", "'"] +[1.1371, "o", "%"] +[1.1722, "o", "h"] +[1.2073, "o", " "] +[1.2424, "o", "%"] +[1.2775, "o", "s"] +[1.3126, "o", "'"] +[1.3477, "o", " "] +[1.3827, "o", "|"] +[1.4178, "o", " "] +[1.4529, "o", "c"] +[1.488, "o", "u"] +[1.5231, "o", "t"] +[1.5582, "o", " "] +[1.5933, "o", "-"] +[1.6284, "o", "c"] +[1.6635, "o", "1"] +[1.6986, "o", "-"] +[1.7337, "o", "1"] +[1.7688, "o", "0"] +[1.8039, "o", "0"] +[2.1391, "o", "\r\n"] +[2.1466, "o", "1a3786657 fix(numeric): ocp_mx compiles natively -- @intCast on runtime indices, no bare signed divi\r\n"] +[2.147, "x", "0"] +[3.1471, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[3.5523, "o", "t"] +[3.5874, "o", "2"] +[3.6225, "o", "7"] +[3.6576, "o", "c"] +[3.6927, "o", " "] +[3.7277, "o", "-"] +[3.7628, "o", "-"] +[3.7979, "o", "v"] +[3.833, "o", "e"] +[3.8681, "o", "r"] +[3.9032, "o", "s"] +[3.9383, "o", "i"] +[3.9734, "o", "o"] +[4.0085, "o", "n"] +[4.0436, "o", " "] +[4.0787, "o", "&"] +[4.1138, "o", "&"] +[4.1489, "o", " "] +[4.184, "o", "z"] +[4.2191, "o", "i"] +[4.2542, "o", "g"] +[4.2893, "o", " "] +[4.3244, "o", "v"] +[4.3595, "o", "e"] +[4.3946, "o", "r"] +[4.4297, "o", "s"] +[4.4648, "o", "i"] +[4.4999, "o", "o"] +[4.535, "o", "n"] +[4.8702, "o", "\r\n"] +[4.8751, "o", "t27c 0.4.0\r\n"] +[4.8783, "o", "0.16.0\r\n"] +[4.8792, "x", "0"] +[5.8793, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[6.2794, "o", "s"] +[6.3145, "o", "e"] +[6.3496, "o", "d"] +[6.3847, "o", " "] +[6.4198, "o", "-"] +[6.4548, "o", "n"] +[6.4899, "o", " "] +[6.525, "o", "6"] +[6.5601, "o", "5"] +[6.5952, "o", "7"] +[6.6303, "o", ","] +[6.6654, "o", "6"] +[6.7005, "o", "6"] +[6.7356, "o", "4"] +[6.7707, "o", "p"] +[6.8058, "o", " "] +[6.8408, "o", "s"] +[6.8759, "o", "p"] +[6.911, "o", "e"] +[6.9461, "o", "c"] +[6.9812, "o", "s"] +[7.0163, "o", "/"] +[7.0513, "o", "n"] +[7.0864, "o", "u"] +[7.1215, "o", "m"] +[7.1566, "o", "e"] +[7.1917, "o", "r"] +[7.2268, "o", "i"] +[7.2619, "o", "c"] +[7.297, "o", "/"] +[7.3321, "o", "o"] +[7.3671, "o", "c"] +[7.4022, "o", "p"] +[7.4373, "o", "_"] +[7.4724, "o", "m"] +[7.5078, "o", "x"] +[7.5428, "o", "."] +[7.5779, "o", "t"] +[7.613, "o", "2"] +[7.6481, "o", "7"] +[7.9833, "o", "\r\n"] +[7.9872, "o", " test small_weights_vanish_in_mxfp4 {\r\n // 31 non-zero inputs; the outlier's scale flushes 23 of them in MXFP4\r\n assert block_flushed(BLOCK_A, K_E2M1) == 23;\r\n assert block_flushed(BLOCK_A, K_E2M3) == 4;\r\n assert block_flushed(BLOCK_A, K_INT8) == 2;\r\n assert block_flushed(BLOCK_A, K_E3M2) == 0;\r\n assert block_flushed(BLOCK_A, K_E4M3) == 0;\r\n }\r\n"] +[7.9872, "x", "0"] +[8.9873, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[9.3874, "o", "t"] +[9.4225, "o", "2"] +[9.4576, "o", "7"] +[9.4927, "o", "c"] +[9.5278, "o", " "] +[9.5646, "o", "t"] +[9.5997, "o", "e"] +[9.6357, "o", "s"] +[9.6708, "o", "t"] +[9.7058, "o", "-"] +[9.7409, "o", "r"] +[9.776, "o", "e"] +[9.8111, "o", "p"] +[9.8462, "o", "o"] +[9.8813, "o", "r"] +[9.917, "o", "t"] +[9.9521, "o", " "] +[9.9872, "o", "s"] +[10.0223, "o", "p"] +[10.0574, "o", "e"] +[10.0926, "o", "c"] +[10.1276, "o", "s"] +[10.1627, "o", "/"] +[10.1978, "o", "n"] +[10.2329, "o", "u"] +[10.268, "o", "m"] +[10.3031, "o", "e"] +[10.3382, "o", "r"] +[10.3733, "o", "i"] +[10.4083, "o", "c"] +[10.4434, "o", "/"] +[10.4785, "o", "o"] +[10.5136, "o", "c"] +[10.5487, "o", "p"] +[10.5838, "o", "_"] +[10.6189, "o", "m"] +[10.654, "o", "x"] +[10.6891, "o", "."] +[10.7242, "o", "t"] +[10.7593, "o", "2"] +[10.7945, "o", "7"] +[10.8295, "o", " "] +[10.8647, "o", "-"] +[10.8998, "o", "-"] +[10.9349, "o", "s"] +[10.97, "o", "p"] +[11.0051, "o", "e"] +[11.0402, "o", "c"] +[11.0753, "o", "s"] +[11.1104, "o", "-"] +[11.1455, "o", "d"] +[11.1806, "o", "i"] +[11.2157, "o", "r"] +[11.2508, "o", " "] +[11.2861, "o", "s"] +[11.3212, "o", "p"] +[11.3563, "o", "e"] +[11.3914, "o", "c"] +[11.4265, "o", "s"] +[11.4616, "o", " "] +[11.4967, "o", "|"] +[11.5318, "o", " "] +[11.5669, "o", "s"] +[11.6019, "o", "e"] +[11.637, "o", "d"] +[11.6721, "o", " "] +[11.7072, "o", "-"] +[11.7423, "o", "n"] +[11.7774, "o", " "] +[11.8125, "o", "'"] +[11.8476, "o", "1"] +[11.8827, "o", ","] +[11.9178, "o", "7"] +[11.9529, "o", "p"] +[11.988, "o", ";"] +[12.0231, "o", "$"] +[12.0582, "o", "p"] +[12.0933, "o", "'"] +[12.4285, "o", "\r\n"] +[12.7988, "o", "--- test report: specs/numeric/ocp_mx.t27 ---\r\n\r\n tests 49\r\n pass 49\r\n FAIL 0\r\n rate 100.0%\r\n\r\n"] +[12.7993, "o", " vacuous passes 0 of 49 (passed with 0 runtime asserts executed)\r\n"] +[12.7997, "x", "0"] +[13.7998, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[14.1999, "o", "t"] +[14.235, "o", "2"] +[14.2701, "o", "7"] +[14.3052, "o", "c"] +[14.3403, "o", " "] +[14.3754, "o", "g"] +[14.4105, "o", "e"] +[14.4456, "o", "n"] +[14.4807, "o", " "] +[14.5158, "o", "s"] +[14.5509, "o", "p"] +[14.586, "o", "e"] +[14.6211, "o", "c"] +[14.6561, "o", "s"] +[14.6912, "o", "/"] +[14.7263, "o", "n"] +[14.7614, "o", "u"] +[14.7965, "o", "m"] +[14.8316, "o", "e"] +[14.8667, "o", "r"] +[14.9018, "o", "i"] +[14.9369, "o", "c"] +[14.972, "o", "/"] +[15.0071, "o", "o"] +[15.0422, "o", "c"] +[15.0773, "o", "p"] +[15.1124, "o", "_"] +[15.1475, "o", "m"] +[15.1826, "o", "x"] +[15.2177, "o", "."] +[15.2528, "o", "t"] +[15.2879, "o", "2"] +[15.323, "o", "7"] +[15.3581, "o", " "] +[15.3932, "o", ">"] +[15.4283, "o", " "] +[15.4634, "o", "o"] +[15.4985, "o", "c"] +[15.5336, "o", "p"] +[15.5687, "o", "_"] +[15.6038, "o", "m"] +[15.6389, "o", "x"] +[15.674, "o", "."] +[15.7091, "o", "z"] +[15.7442, "o", "i"] +[15.7793, "o", "g"] +[15.8144, "o", " "] +[15.8495, "o", "&"] +[15.8846, "o", "&"] +[15.9197, "o", " "] +[15.9548, "o", "z"] +[15.9899, "o", "i"] +[16.025, "o", "g"] +[16.0601, "o", " "] +[16.0952, "o", "t"] +[16.1303, "o", "e"] +[16.1654, "o", "s"] +[16.2005, "o", "t"] +[16.2357, "o", " "] +[16.2708, "o", "o"] +[16.3059, "o", "c"] +[16.341, "o", "p"] +[16.3761, "o", "_"] +[16.4112, "o", "m"] +[16.4464, "o", "x"] +[16.4815, "o", "."] +[16.5166, "o", "z"] +[16.5517, "o", "i"] +[16.5868, "o", "g"] +[16.922, "o", "\r\n"] +[16.9462, "o", "All 49 tests passed.\r\n"] +[16.9477, "x", "0"] +[17.9478, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[18.3479, "o", "s"] +[18.383, "o", "e"] +[18.4181, "o", "d"] +[18.4532, "o", " "] +[18.4882, "o", "-"] +[18.5233, "o", "i"] +[18.5584, "o", " "] +[18.5935, "o", "'"] +[18.6286, "o", "s"] +[18.6637, "o", "/"] +[18.6988, "o", "="] +[18.7339, "o", " "] +[18.769, "o", "\\"] +[18.8041, "o", "["] +[18.8392, "o", "1"] +[18.8743, "o", ","] +[18.9094, "o", " "] +[18.9485, "o", "1"] +[18.9836, "o", ","] +[19.0187, "o", " "] +[19.0538, "o", "3"] +[19.0889, "o", ","] +[19.124, "o", " "] +[19.1591, "o", "7"] +[19.1941, "o", ","] +[19.2292, "o", " "] +[19.2643, "o", "1"] +[19.2994, "o", "5"] +[19.3345, "o", ","] +[19.3696, "o", " "] +[19.4047, "o", "0"] +[19.4398, "o", "\\"] +[19.4748, "o", "]"] +[19.5099, "o", "/"] +[19.545, "o", "="] +[19.5801, "o", " "] +[19.6152, "o", "["] +[19.6503, "o", "1"] +[19.6854, "o", ","] +[19.7205, "o", " "] +[19.7556, "o", "1"] +[19.7907, "o", ","] +[19.8257, "o", " "] +[19.8608, "o", "3"] +[19.8959, "o", ","] +[19.931, "o", " "] +[19.9661, "o", "7"] +[20.0012, "o", ","] +[20.0363, "o", " "] +[20.0714, "o", "1"] +[20.1065, "o", "6"] +[20.1416, "o", ","] +[20.1766, "o", " "] +[20.2117, "o", "0"] +[20.2468, "o", "]"] +[20.2819, "o", "/"] +[20.317, "o", "'"] +[20.3521, "o", " "] +[20.3872, "o", "s"] +[20.4223, "o", "p"] +[20.4574, "o", "e"] +[20.4925, "o", "c"] +[20.5276, "o", "s"] +[20.5627, "o", "/"] +[20.5978, "o", "n"] +[20.6329, "o", "u"] +[20.668, "o", "m"] +[20.7031, "o", "e"] +[20.7382, "o", "r"] +[20.7733, "o", "i"] +[20.8084, "o", "c"] +[20.8435, "o", "/"] +[20.8785, "o", "o"] +[20.9136, "o", "c"] +[20.9487, "o", "p"] +[20.9838, "o", "_"] +[21.0189, "o", "m"] +[21.054, "o", "x"] +[21.0891, "o", "."] +[21.1242, "o", "t"] +[21.1593, "o", "2"] +[21.1943, "o", "7"] +[21.2294, "o", " "] +[21.2645, "o", "&"] +[21.2996, "o", "&"] +[21.3347, "o", " "] +[21.3698, "o", "g"] +[21.4049, "o", "i"] +[21.44, "o", "t"] +[21.4751, "o", " "] +[21.5102, "o", "d"] +[21.5453, "o", "i"] +[21.5804, "o", "f"] +[21.6154, "o", "f"] +[21.6505, "o", " "] +[21.6856, "o", "-"] +[21.7207, "o", "U"] +[21.7557, "o", "0"] +[21.7909, "o", " "] +[21.8259, "o", "|"] +[21.861, "o", " "] +[21.8961, "o", "t"] +[21.9311, "o", "a"] +[21.9662, "o", "i"] +[22.0013, "o", "l"] +[22.0364, "o", " "] +[22.0715, "o", "-"] +[22.1066, "o", "n"] +[22.1416, "o", " "] +[22.1767, "o", "2"] +[22.5119, "o", "\r\n"] +[22.5211, "o", "- const KIND_BIAS : [6]i64 = [1, 1, 3, 7, 15, 0];\r\n+ const KIND_BIAS : [6]i64 = [1, 1, 3, 7, 16, 0];\r\n"] +[22.5213, "x", "0"] +[23.5214, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[23.9215, "o", "t"] +[23.9566, "o", "2"] +[23.9917, "o", "7"] +[24.0268, "o", "c"] +[24.0619, "o", " "] +[24.097, "o", "t"] +[24.1321, "o", "e"] +[24.1672, "o", "s"] +[24.2023, "o", "t"] +[24.2373, "o", "-"] +[24.2724, "o", "r"] +[24.3075, "o", "e"] +[24.3426, "o", "p"] +[24.3777, "o", "o"] +[24.4128, "o", "r"] +[24.4479, "o", "t"] +[24.483, "o", " "] +[24.5181, "o", "s"] +[24.5531, "o", "p"] +[24.5882, "o", "e"] +[24.6233, "o", "c"] +[24.6584, "o", "s"] +[24.6935, "o", "/"] +[24.729, "o", "n"] +[24.7641, "o", "u"] +[24.7992, "o", "m"] +[24.8343, "o", "e"] +[24.8694, "o", "r"] +[24.9045, "o", "i"] +[24.9396, "o", "c"] +[24.9747, "o", "/"] +[25.0098, "o", "o"] +[25.0449, "o", "c"] +[25.0799, "o", "p"] +[25.115, "o", "_"] +[25.1501, "o", "m"] +[25.1852, "o", "x"] +[25.2203, "o", "."] +[25.2554, "o", "t"] +[25.2905, "o", "2"] +[25.3256, "o", "7"] +[25.3606, "o", " "] +[25.3957, "o", "-"] +[25.4308, "o", "-"] +[25.4659, "o", "s"] +[25.501, "o", "p"] +[25.536, "o", "e"] +[25.5711, "o", "c"] +[25.6062, "o", "s"] +[25.6413, "o", "-"] +[25.6764, "o", "d"] +[25.7115, "o", "i"] +[25.7466, "o", "r"] +[25.7817, "o", " "] +[25.8168, "o", "s"] +[25.8518, "o", "p"] +[25.8869, "o", "e"] +[25.9221, "o", "c"] +[25.9571, "o", "s"] +[25.9923, "o", " "] +[26.0273, "o", "|"] +[26.0624, "o", " "] +[26.0975, "o", "s"] +[26.1326, "o", "e"] +[26.1683, "o", "d"] +[26.2034, "o", " "] +[26.2385, "o", "-"] +[26.2736, "o", "n"] +[26.3087, "o", " "] +[26.3438, "o", "1"] +[26.3789, "o", ","] +[26.414, "o", "8"] +[26.4491, "o", "p"] +[26.7843, "o", "\r\n"] +[27.3358, "o", "--- test report: specs/numeric/ocp_mx.t27 ---\r\n FAIL bias_puts_the_min_normal_at_two_to_one_minus_bias\r\n\r\n tests 49\r\n pass 48\r\n FAIL 1\r\n rate 98.0%\r\n\r\n"] +[27.3366, "x", "0"] +[28.3367, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] +[28.7368, "o", "g"] +[28.7719, "o", "i"] +[28.807, "o", "t"] +[28.8421, "o", " "] +[28.8772, "o", "c"] +[28.9123, "o", "h"] +[28.9474, "o", "e"] +[28.9825, "o", "c"] +[29.0176, "o", "k"] +[29.0527, "o", "o"] +[29.0878, "o", "u"] +[29.1229, "o", "t"] +[29.158, "o", " "] +[29.1931, "o", "s"] +[29.2282, "o", "p"] +[29.2633, "o", "e"] +[29.2983, "o", "c"] +[29.3334, "o", "s"] +[29.3685, "o", "/"] +[29.4036, "o", "n"] +[29.4387, "o", "u"] +[29.4738, "o", "m"] +[29.5089, "o", "e"] +[29.544, "o", "r"] +[29.5791, "o", "i"] +[29.6142, "o", "c"] +[29.6492, "o", "/"] +[29.6843, "o", "o"] +[29.7194, "o", "c"] +[29.7545, "o", "p"] +[29.7896, "o", "_"] +[29.8247, "o", "m"] +[29.8598, "o", "x"] +[29.8949, "o", "."] +[29.93, "o", "t"] +[29.9651, "o", "2"] +[30.0001, "o", "7"] +[30.0352, "o", " "] +[30.0703, "o", "&"] +[30.1054, "o", "&"] +[30.1405, "o", " "] +[30.1756, "o", "s"] +[30.2107, "o", "h"] +[30.2458, "o", "a"] +[30.2809, "o", "2"] +[30.316, "o", "5"] +[30.3511, "o", "6"] +[30.3862, "o", "s"] +[30.4212, "o", "u"] +[30.4563, "o", "m"] +[30.4914, "o", " "] +[30.5266, "o", "s"] +[30.5617, "o", "p"] +[30.5968, "o", "e"] +[30.6319, "o", "c"] +[30.6669, "o", "s"] +[30.702, "o", "/"] +[30.7371, "o", "n"] +[30.7722, "o", "u"] +[30.8073, "o", "m"] +[30.8424, "o", "e"] +[30.8775, "o", "r"] +[30.9125, "o", "i"] +[30.9478, "o", "c"] +[30.9829, "o", "/"] +[31.0179, "o", "o"] +[31.053, "o", "c"] +[31.0881, "o", "p"] +[31.1232, "o", "_"] +[31.1583, "o", "m"] +[31.1934, "o", "x"] +[31.2287, "o", "."] +[31.2638, "o", "t"] +[31.2989, "o", "2"] +[31.334, "o", "7"] +[31.6691, "o", "\r\n"] +[31.6772, "o", "Updated 1 path from the index\r\n"] +[31.6857, "o", "b67eb3b0586958d9afb84059ea18eca3765f013ef66bbb7574a1f9f165e7139a specs/numeric/ocp_mx.t27\r\n"] +[31.6858, "x", "0"] +[32.6859, "o", "\u001b[38;2;255;215;0mt27 $ \u001b[0m"] diff --git a/apps/website/public/term/t27c-ocp-mx/session.gif b/apps/website/public/term/t27c-ocp-mx/session.gif new file mode 100644 index 0000000000..2ab70a47e6 Binary files /dev/null and b/apps/website/public/term/t27c-ocp-mx/session.gif differ diff --git a/apps/website/public/widgets/gallery.t27 b/apps/website/public/widgets/gallery.t27 index 3f740c591e..f5746d3af1 100644 --- a/apps/website/public/widgets/gallery.t27 +++ b/apps/website/public/widgets/gallery.t27 @@ -78,15 +78,15 @@ pub const TOOL_NOSCRIPT : str = "This widget runs in your browser and needs Java ; Parallel arrays, WIDGET_COUNT long. WIDGET_TITLES is empty wherever the page carries an ; og:title, which is every kind but tab: the page is the one place the words live. A tool's ; WIDGET_HOOKS entry is empty too: its hook is the page's t27:hook, written from its own spec. -pub const WIDGET_COUNT : u8 = 45; -pub const WIDGET_IDS : [45]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle"]; -pub const WIDGET_KINDS : [45]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "tab", "cast", "cast", "cast", "tool", "tool"]; -pub const WIDGET_CATEGORIES : [45]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game"]; -pub const WIDGET_PAGES : [45]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/"]; -pub const WIDGET_TITLES : [45]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", ""]; -pub const WIDGET_LINES : [45]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", ""]; +pub const WIDGET_COUNT : u8 = 48; +pub const WIDGET_IDS : [48]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "t27c-e8m0", "play-e8m0", "t27c-ocp-mx", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle"]; +pub const WIDGET_KINDS : [48]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "cast", "player", "cast", "tab", "cast", "cast", "cast", "tool", "tool"]; +pub const WIDGET_CATEGORIES : [48]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game"]; +pub const WIDGET_PAGES : [48]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "term/t27c-e8m0/", "play/e8m0/", "term/t27c-ocp-mx/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/"]; +pub const WIDGET_TITLES : [48]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", ""]; +pub const WIDGET_LINES : [48]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", ""]; ; Why a person would pass this one on: the reason the widget is in the gallery at all. -pub const WIDGET_HOOKS : [45]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", ""]; +pub const WIDGET_HOOKS : [48]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", ""]; ; --- Ideas: asked for, not built --------------------------------------------------------------- ; From what engineers share today (gh-skyline, Tiny Tapeout renders, Wrapped cards, Gatle-style @@ -119,26 +119,34 @@ test the_fpga_shelf_comes_first_and_opens_on_the_board { } test the_arena_is_a_tab_and_says_its_own_words { - assert WIDGET_IDS[39] == "wars"; - assert WIDGET_KINDS[39] == "tab"; - assert WIDGET_PAGES[39] == "wars"; + assert WIDGET_IDS[42] == "wars"; + assert WIDGET_KINDS[42] == "tab"; + assert WIDGET_PAGES[42] == "wars"; } test the_gallery_posts_nothing { assert POSTS_NOTHING == true; } +test the_ai_number_shelf_closes_the_compiler_run { + assert WIDGET_IDS[39] == "t27c-e8m0"; + assert WIDGET_IDS[40] == "play-e8m0"; + assert WIDGET_PAGES[40] == "play/e8m0/"; + assert WIDGET_IDS[41] == "t27c-ocp-mx"; + assert WIDGET_CATEGORIES[41] == "compiler"; +} + test ideas_are_counted_apart_from_widgets { assert IDEA_COUNT == 6; - assert WIDGET_IDS[44] == "gatle"; - assert WIDGET_COUNT == 45; + assert WIDGET_IDS[47] == "gatle"; + assert WIDGET_COUNT == 48; } test every_tool_lives_under_widgets_and_keeps_its_words_on_its_page { assert WIDGET_KINDS[9] == "tool"; assert WIDGET_PAGES[9] == "widgets/bit-diff/"; assert WIDGET_HOOKS[9] == ""; - assert WIDGET_KINDS[43] == "tool"; - assert WIDGET_CATEGORIES[43] == "game"; - assert WIDGET_PAGES[43] == "widgets/race-the-bee/"; + assert WIDGET_KINDS[46] == "tool"; + assert WIDGET_CATEGORIES[46] == "game"; + assert WIDGET_PAGES[46] == "widgets/race-the-bee/"; } diff --git a/apps/website/scripts/course-from-spec.test.mjs b/apps/website/scripts/course-from-spec.test.mjs index 621eebdc95..ac50a96837 100644 --- a/apps/website/scripts/course-from-spec.test.mjs +++ b/apps/website/scripts/course-from-spec.test.mjs @@ -20,8 +20,6 @@ test('the committed spec builds clean and the derived files are current', async assert.deepEqual(out.problems, []) assert.equal(readFileSync(join(SITE, TS_OUT), 'utf8'), out.ts) assert.equal(readFileSync(join(SITE, PUBLIC_SPEC_OUT), 'utf8'), inputs.specText) - assert.equal(out.course.lessons.length, 30) - assert.equal(out.course.modules.length, 10) assert.equal(out.course.sendsNothing, true) }) diff --git a/apps/website/specs/course/course.t27 b/apps/website/specs/course/course.t27 index c94efea094..8b0f668415 100644 --- a/apps/website/specs/course/course.t27 +++ b/apps/website/specs/course/course.t27 @@ -10,7 +10,8 @@ ; ASCII only (L3), English only (LANG-EN). The Russian words live in the bundle. ; WHAT THE COURSE IS: 27 lessons in 9 modules of 3, for a reader who has never written for a -; chip, then a lab module of 3 lessons on our own research. Each lesson is a short text, one working widget from the gallery, and one thing to try +; chip, then a lab module of 3 lessons on our own research, then a module of 3 on the MX number +; formats AI chips use (OCP Microscaling v1.0). Each lesson is a short text, one working widget from the gallery, and one thing to try ; in it. The widgets are not drawn for the course: they are the same tools a hardware engineer ; uses on t27.ai, built from real runs of t27c, yosys, nextpnr and openFPGALoader on our bench. @@ -18,9 +19,13 @@ ; widget, which names where it came from. The one exception is the t27b figure in lesson 12, ; quoted with its denominator and the run that produced it. The lab lessons quote only the ; rule their widget tests (990 thousandths) and counts that are arithmetic (3 x 3 x 3 = 27). +; The MX lessons quote the constants of the OCP MX v1.0 standard that their widgets compile +; (a block of 32, element widths of 4, 6 and 8 bits, scale code 255 for NaN, bias 127) and +; counts their recording prints. ; WHY 27: the core is 3 cubed lessons, the size of a TRI-27 word. The lab is one more module -; of 3 that reports our own results; it is counted apart so the core stays 27. Changing the +; of 3 that reports our own results, and the MX module one more of 3 on the formats the lab +; compares against; both are counted apart so the core stays 27. Changing the ; shape changes the tests. ; phi^2 + 1/phi^2 = 3 | TRINITY @@ -47,10 +52,10 @@ pub const PLAYER_FRAME_HEIGHT : u16 = 480; ; --- Words on the page --------------------------------------------------------------------- pub const TITLE : str = "From zero to a chip: FPGA programming with t27"; -pub const DESCRIPTION : str = "27 lessons, one working widget each: what an FPGA is, how numbers become bits, your own t27 spec, how t27c compiles it, and how it becomes a bitstream on a real board. Then a lab of 3 on our own research."; +pub const DESCRIPTION : str = "27 lessons, one working widget each: what an FPGA is, how numbers become bits, your own t27 spec, how t27c compiles it, and how it becomes a bitstream on a real board. Then a lab of 3 on our own research, and 3 lessons on the MX number formats AI chips use."; pub const SAY_KICKER : str = "Course"; pub const SAY_LEAD : str = "No hardware background needed. Every lesson opens a real tool built from real runs on our bench: read a little, then try it yourself."; -pub const SAY_SHAPE : str = "9 modules of 3 lessons, 27 cells, then a lab of 3. A filled cell is a lesson you marked done."; +pub const SAY_SHAPE : str = "9 modules of 3 lessons, 27 cells, then a lab of 3 and an AI numbers module of 3. A filled cell is a lesson you marked done."; pub const SAY_START : str = "Start lesson 1"; pub const SAY_CONTINUE : str = "Continue"; pub const SAY_MODULE : str = "Module {0}"; @@ -80,10 +85,11 @@ pub const SAY_SHARE : str = "Link to share, with a preview card"; ; --- Modules ------------------------------------------------------------------------------- pub const LESSONS_PER_MODULE : u8 = 3; -; The core course is the first 9 modules, 3 cubed lessons; the lab is the 10th module. -pub const MODULE_COUNT : u8 = 10; -pub const MODULE_IDS : [10]str = ["chip", "numbers", "program", "compiler", "hardware", "synthesis", "timing", "bitstream", "board", "lab"]; -pub const MODULE_TITLES : [10]str = [ +; The core course is the first 9 modules, 3 cubed lessons; the lab is the 10th module and the +; MX number formats the 11th. +pub const MODULE_COUNT : u8 = 11; +pub const MODULE_IDS : [11]str = ["chip", "numbers", "program", "compiler", "hardware", "synthesis", "timing", "bitstream", "board", "lab", "mx"]; +pub const MODULE_TITLES : [11]str = [ "The chip", "Numbers in hardware", "Your t27 program", @@ -93,9 +99,10 @@ pub const MODULE_TITLES : [10]str = [ "Place, route, timing", "The bitstream", "On the board", - "Lab: our own research" + "Lab: our own research", + "AI numbers: the MX block" ]; -pub const MODULE_LINES : [10]str = [ +pub const MODULE_LINES : [11]str = [ "What an FPGA is, which chip we use, and how its pins meet the board.", "Bits, trits and number formats, and what arithmetic costs in logic.", "Write a spec, test it, and see why compiling is not the same as being right.", @@ -105,7 +112,8 @@ pub const MODULE_LINES : [10]str = [ "Where the cells land on the die, and whether the clock is met.", "How a routed design becomes the bits the chip loads, with no Vivado.", "Ask the chip who it is, load the bits, and check them.", - "A number format of our own, an honest scoreboard, and a model's tables multiplied on the board." + "A number format of our own, an honest scoreboard, and a model's tables multiplied on the board.", + "How AI chips keep weights in a few bits: one shared scale per block, the scale byte itself, and what one outlier does to its neighbours." ]; ; --- Lessons ------------------------------------------------------------------------------- @@ -113,8 +121,8 @@ pub const MODULE_LINES : [10]str = [ ; lessons open the same one. LESSON_ALSO lists more gallery ids, comma separated, shown as ; chips that swap the widget. LESSON_SPECS is a spec under public/t27/files/ to open in the ; player, or "" for none. -pub const LESSON_COUNT : u8 = 30; -pub const LESSON_IDS : [30]str = [ +pub const LESSON_COUNT : u8 = 33; +pub const LESSON_IDS : [33]str = [ "fpga-is-a-grid", "which-chip", "pins-and-package", "bits-and-trits", "what-arithmetic-costs", "how-much-fits", "hello-t27", "tests-are-the-spec", "compiles-is-not-correct", @@ -124,9 +132,10 @@ pub const LESSON_IDS : [30]str = [ "placement", "logic-depth", "timing-slack", "fasm-to-frames", "a-build-receipt", "read-a-bitstream", "jtag-asks-the-chip", "same-bits-on-the-board", "capstone", - "t27-number-format", "an-honest-scoreboard", "a-model-on-the-board" + "t27-number-format", "an-honest-scoreboard", "a-model-on-the-board", + "one-scale-per-block", "scale-byte-native", "one-outlier" ]; -pub const LESSON_MODULES : [30]str = [ +pub const LESSON_MODULES : [33]str = [ "chip", "chip", "chip", "numbers", "numbers", "numbers", "program", "program", "program", @@ -136,9 +145,10 @@ pub const LESSON_MODULES : [30]str = [ "timing", "timing", "timing", "bitstream", "bitstream", "bitstream", "board", "board", "board", - "lab", "lab", "lab" + "lab", "lab", "lab", + "mx", "mx", "mx" ]; -pub const LESSON_WIDGETS : [30]str = [ +pub const LESSON_WIDGETS : [33]str = [ "fasm-skyline", "idcode", "pin-map", "gf16-calc", "cost-curves", "fits-on", "play-hello-world", "test-waves", "t27-tri-misread", @@ -148,9 +158,10 @@ pub const LESSON_WIDGETS : [30]str = [ "placement-map", "lut-depth", "slack-waterfall", "tri-x7-fasm", "build-receipt", "bit-autopsy", "tri-fpga-jtag", "x7-board", "gatle", - "t27-cell", "t27-vs-nvfp4", "uart-bucket" + "t27-cell", "t27-vs-nvfp4", "uart-bucket", + "play-e8m0", "t27c-e8m0", "t27c-ocp-mx" ]; -pub const LESSON_ALSO : [30]str = [ +pub const LESSON_ALSO : [33]str = [ "placement-map", "tri-fpga-jtag", "tri-fpga-pins", "", "resource-bars", "", "play-js", "vcd-wrapped", "tri-x7-mutate,test-touch", @@ -160,9 +171,10 @@ pub const LESSON_ALSO : [30]str = [ "resource-bars", "", "tri-fpga-rgmii", "", "perf2-loop,devkit-flow", "perf2-knockouts", "idcode", "bit-diff", "race-the-bee,wars,tri-game-tick,tri-game-vault", - "gf16-calc", "t27-cell", "x7-board" + "gf16-calc", "t27-cell", "x7-board", + "", "", "t27-vs-nvfp4" ]; -pub const LESSON_SPECS : [30]str = [ +pub const LESSON_SPECS : [33]str = [ "", "", "", "specs/tutorial/01_values_and_types.t27", "specs/tutorial/05_widths_and_casts.t27", "", "specs/tutorial/02_functions.t27", "specs/tutorial/07_tests_invariants_benches.t27", "specs/tutorial/03_operators.t27", @@ -172,9 +184,10 @@ pub const LESSON_SPECS : [30]str = [ "", "", "", "", "", "", "", "", "", - "", "", "" + "", "", "", + "", "specs/numeric/e8m0.t27", "" ]; -pub const LESSON_TITLES : [30]str = [ +pub const LESSON_TITLES : [33]str = [ "An FPGA is a grid you configure", "Which chip is this?", "Pins, balls and the package", @@ -204,9 +217,12 @@ pub const LESSON_TITLES : [30]str = [ "Capstone: out-guess yosys", "T27: a number format of our own", "An honest scoreboard", - "A model's tables on the board" + "A model's tables on the board", + "One scale for a block of weights", + "The scale byte on a real machine", + "One outlier, many zeros" ]; -pub const LESSON_GOALS : [30]str = [ +pub const LESSON_GOALS : [33]str = [ "What an FPGA is made of, and why a design uses only part of the chip.", "How a chip names itself in 32 bits, and how to read those bits.", "How the signals of your design reach the outside world.", @@ -236,9 +252,12 @@ pub const LESSON_GOALS : [30]str = [ "Put it together: guess what a real t27 module costs in LUTs.", "How three trits give more levels than a 4-bit float, and what that costs in plain bits.", "How T27 was compared with NVFP4, MX+ and MXFP4 on a real language model, and what the result does not show.", - "How the host feeds the board over a serial bridge, why the window size matters, and what the board computed." + "How the host feeds the board over a serial bridge, why the window size matters, and what the board computed.", + "Why AI formats share one scale across a block of weights, and how the scale byte of OCP MX is read.", + "What the native compiler checks in the same spec, including what the browser skipped.", + "What a shared scale costs: how one large weight pushes the small weights of its block to zero, format by format." ]; -pub const LESSON_TEXTS : [30]str = [ +pub const LESSON_TEXTS : [33]str = [ "A CPU runs instructions one after another. An FPGA has no program at all: it is a grid of small blocks -- lookup tables (LUTs), flip-flops, carry chains, block RAM and DSP multipliers -- joined by wires you can switch. Configuring the grid builds a circuit. Our bench chip is an AMD Artix-7 XC7A200T. The skyline draws one routed design on that die: every tile it configures stands up as a column, and most of the chip stays dark.", "Before you load anything, you ask the board which chip it has. Over JTAG every chip answers with a 32-bit IDCODE: a version, a part number, the manufacturer and a fixed 1 in bit 0. A bitstream built for the wrong part is refused by the chip, so this is the check that saves you an afternoon. The decoder splits the bits into those four fields and looks the part up in openFPGALoader's own table.", "A chip sits in a package: the XC7A200T on our board has 676 solder balls under it. Some carry power or configuration and only some are user IO, grouped into banks with their own voltage. A constraints file (XDC) says which ball each signal of your design uses. Put a signal on a ball that is not user IO and the build fails, or worse, the board does not do what you think.", @@ -268,9 +287,12 @@ pub const LESSON_TEXTS : [30]str = [ "Everything from this course in one game. Each day Gatle shows one real t27 module and the Verilog your browser compiles from it; you have six guesses at how many LUTs yosys spent on it. Use what you know: widths, multipliers, hierarchy, state. Then look at the other ways people and agents compete on t27.", "A 4-bit float such as E2M1, the element of NVFP4 and MXFP4, has 16 codes, and two of them are zero. Three balanced trits have 3 x 3 x 3 = 27 states. T27 is our format that uses all of them: 27 levels placed where a trained network's weights actually fall, and one shared scale per block of weights. On ternary storage that is more levels in the same room. Packed into plain binary bits it is larger, not smaller, and the widget shows both counts.", "A format is judged by what it does to a model. We stored the weights of one small language model in each format and measured perplexity on text it had not seen: lower is better. The rule was written before the run: T27 beats a rival when its perplexity is below 990 thousandths of the rival's. It beat NVFP4, MX+ and MXFP4, and it lost to E2M2, a wider format. The panel under the bars lists what this test cannot claim. The one claim that holds: T27 beats NVFP4 on a ternary substrate.", - "The board does the arithmetic; the host sends it work over a USB serial bridge. The board's answers wait in the bridge chip's small receive buffer, and when the host keeps more requests in flight than their answers fit, answers are lost. A smaller window removed the loss and did not slow the run. Over that link the board multiplied every ternary weight matrix of a small language model, and every row matched the reference bit for bit. The text itself is generated on the Mac, which does everything except the ternary dot products: the board is the calculator, not the whole model." + "The board does the arithmetic; the host sends it work over a USB serial bridge. The board's answers wait in the bridge chip's small receive buffer, and when the host keeps more requests in flight than their answers fit, answers are lost. A smaller window removed the loss and did not slow the run. Over that link the board multiplied every ternary weight matrix of a small language model, and every row matched the reference bit for bit. The text itself is generated on the Mac, which does everything except the ternary dot products: the board is the calculator, not the whole model.", + "A language model has millions of weights, and a full float for each is expensive to store and to move. The Microscaling (MX) formats of the Open Compute Project keep each weight in 4, 6 or 8 bits and give every block of 32 weights one shared scale. That scale is a single byte called E8M0: no sign and no mantissa, only an exponent, so code e means 2 to the power e minus 127, and code 255 means NaN. The player compiles e8m0.t27, the spec of that byte, in your browser and runs its tests. Where the browser cannot run a check, it skips it and says why.", + "The player in the last lesson could not run every check in e8m0.t27, and it said so. This recording runs the native t27c and Zig on a real machine on the same spec: its constants, the test report, the Zig tests, and the Verilog for the NaN check. Some checks are invariants the compiler proves while it compiles, so compiling is the check. Every byte in the recording was printed by the command; only the typing is staged.", + "A shared scale is set by the largest value in its block, so one outlier sets it for all 32 weights. Weights much smaller than the outlier then fall below the smallest step the element can show and are stored as zero. This recording runs ocp_mx.t27, our spec of OCP MX v1.0, natively on a block of 31 small weights and one outlier, and counts how many each element format flushes to zero. Then it plants a bug, one changed exponent bias, and exactly one test fails: a spec that carries its tests catches its own mistakes." ]; -pub const LESSON_TASKS : [30]str = [ +pub const LESSON_TASKS : [33]str = [ "Rotate the die and tap the tallest column: which kind of tile sets the most features?", "Click the bench preset, then flip one bit of the part number and see which device it would be.", "Search for a ball name like E3, then colour by pin type and find a ball that is not user IO.", @@ -300,15 +322,18 @@ pub const LESSON_TASKS : [30]str = [ "Play today's puzzle. Then read the module's Verilog and say why it cost what it did.", "Move the weight across the range and find where E2M1 rounds worse than T27; then read the storage table in bits.", "Find the NVFP4 ratio and the threshold line; then name the format T27 loses to and what it pays for that.", - "Drag the window until the bucket overflows and note the largest window that fits; then compare the two real runs." + "Drag the window until the bucket overflows and note the largest window that fits; then compare the two real runs.", + "Run the tests and count the skips; then open the Verilog and find the function that checks for code 255.", + "Find how many invariants the test report proved at compile time, and compare its pass count with the player's.", + "Read which element format flushes the most small weights to zero and which two flush none; then find the test the planted bug breaks." ]; ; --- Claims -------------------------------------------------------------------------------- test the_core_is_three_cubed { assert LESSONS_PER_MODULE == 3; - assert MODULE_COUNT == 10; - assert LESSON_COUNT == 30; - assert LESSON_COUNT - LESSONS_PER_MODULE == 27; + assert MODULE_COUNT == 11; + assert LESSON_COUNT == 33; + assert LESSON_COUNT - 2 * LESSONS_PER_MODULE == 27; assert MODULE_COUNT * LESSONS_PER_MODULE == LESSON_COUNT; } @@ -327,6 +352,16 @@ test the_lab_comes_after_the_core { assert LESSON_WIDGETS[27] == "t27-cell"; } +test the_mx_module_comes_after_the_lab { + assert MODULE_IDS[10] == "mx"; + assert LESSON_MODULES[30] == "mx"; + assert LESSON_MODULES[32] == "mx"; + assert LESSON_WIDGETS[30] == "play-e8m0"; + assert LESSON_WIDGETS[31] == "t27c-e8m0"; + assert LESSON_WIDGETS[32] == "t27c-ocp-mx"; + assert LESSON_SPECS[31] == "specs/numeric/e8m0.t27"; +} + test the_compiler_module_opens_the_compiler { assert LESSON_MODULES[9] == "compiler"; assert LESSON_WIDGETS[9] == "t27c-stages"; diff --git a/apps/website/specs/widgets/gallery.t27 b/apps/website/specs/widgets/gallery.t27 index 3f740c591e..f5746d3af1 100644 --- a/apps/website/specs/widgets/gallery.t27 +++ b/apps/website/specs/widgets/gallery.t27 @@ -78,15 +78,15 @@ pub const TOOL_NOSCRIPT : str = "This widget runs in your browser and needs Java ; Parallel arrays, WIDGET_COUNT long. WIDGET_TITLES is empty wherever the page carries an ; og:title, which is every kind but tab: the page is the one place the words live. A tool's ; WIDGET_HOOKS entry is empty too: its hook is the page's t27:hook, written from its own spec. -pub const WIDGET_COUNT : u8 = 45; -pub const WIDGET_IDS : [45]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle"]; -pub const WIDGET_KINDS : [45]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "tab", "cast", "cast", "cast", "tool", "tool"]; -pub const WIDGET_CATEGORIES : [45]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game"]; -pub const WIDGET_PAGES : [45]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/"]; -pub const WIDGET_TITLES : [45]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", ""]; -pub const WIDGET_LINES : [45]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", ""]; +pub const WIDGET_COUNT : u8 = 48; +pub const WIDGET_IDS : [48]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "t27c-e8m0", "play-e8m0", "t27c-ocp-mx", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle"]; +pub const WIDGET_KINDS : [48]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "cast", "player", "cast", "tab", "cast", "cast", "cast", "tool", "tool"]; +pub const WIDGET_CATEGORIES : [48]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game"]; +pub const WIDGET_PAGES : [48]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "term/t27c-e8m0/", "play/e8m0/", "term/t27c-ocp-mx/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/"]; +pub const WIDGET_TITLES : [48]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", ""]; +pub const WIDGET_LINES : [48]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", ""]; ; Why a person would pass this one on: the reason the widget is in the gallery at all. -pub const WIDGET_HOOKS : [45]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", ""]; +pub const WIDGET_HOOKS : [48]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", ""]; ; --- Ideas: asked for, not built --------------------------------------------------------------- ; From what engineers share today (gh-skyline, Tiny Tapeout renders, Wrapped cards, Gatle-style @@ -119,26 +119,34 @@ test the_fpga_shelf_comes_first_and_opens_on_the_board { } test the_arena_is_a_tab_and_says_its_own_words { - assert WIDGET_IDS[39] == "wars"; - assert WIDGET_KINDS[39] == "tab"; - assert WIDGET_PAGES[39] == "wars"; + assert WIDGET_IDS[42] == "wars"; + assert WIDGET_KINDS[42] == "tab"; + assert WIDGET_PAGES[42] == "wars"; } test the_gallery_posts_nothing { assert POSTS_NOTHING == true; } +test the_ai_number_shelf_closes_the_compiler_run { + assert WIDGET_IDS[39] == "t27c-e8m0"; + assert WIDGET_IDS[40] == "play-e8m0"; + assert WIDGET_PAGES[40] == "play/e8m0/"; + assert WIDGET_IDS[41] == "t27c-ocp-mx"; + assert WIDGET_CATEGORIES[41] == "compiler"; +} + test ideas_are_counted_apart_from_widgets { assert IDEA_COUNT == 6; - assert WIDGET_IDS[44] == "gatle"; - assert WIDGET_COUNT == 45; + assert WIDGET_IDS[47] == "gatle"; + assert WIDGET_COUNT == 48; } test every_tool_lives_under_widgets_and_keeps_its_words_on_its_page { assert WIDGET_KINDS[9] == "tool"; assert WIDGET_PAGES[9] == "widgets/bit-diff/"; assert WIDGET_HOOKS[9] == ""; - assert WIDGET_KINDS[43] == "tool"; - assert WIDGET_CATEGORIES[43] == "game"; - assert WIDGET_PAGES[43] == "widgets/race-the-bee/"; + assert WIDGET_KINDS[46] == "tool"; + assert WIDGET_CATEGORIES[46] == "game"; + assert WIDGET_PAGES[46] == "widgets/race-the-bee/"; } diff --git a/apps/website/specs/x/player.t27 b/apps/website/specs/x/player.t27 index efac42b7e6..1775e9e0dd 100644 --- a/apps/website/specs/x/player.t27 +++ b/apps/website/specs/x/player.t27 @@ -43,7 +43,7 @@ pub const NAME : str = "t27 in a post"; ; The derived files the generator rewrites (relative to apps/website): this spec's public copy, ; then the site modules below as one ES module, then per play, in PLAY_IDS order, its share page ; and one share page per backend, in BACKENDS order. -pub const GENERATED : [10]str = ["public/play/player.t27", "public/play/site.js", "public/play/hello-world/index.html", "public/play/hello-world/zig/index.html", "public/play/hello-world/c/index.html", "public/play/hello-world/rust/index.html", "public/play/hello-world/verilog/index.html", "public/play/hello-world/verilog-hir/index.html", "public/play/hello-world/js/index.html", "public/play/hello-world/ts/index.html"]; +pub const GENERATED : [18]str = ["public/play/player.t27", "public/play/site.js", "public/play/hello-world/index.html", "public/play/hello-world/zig/index.html", "public/play/hello-world/c/index.html", "public/play/hello-world/rust/index.html", "public/play/hello-world/verilog/index.html", "public/play/hello-world/verilog-hir/index.html", "public/play/hello-world/js/index.html", "public/play/hello-world/ts/index.html", "public/play/e8m0/index.html", "public/play/e8m0/zig/index.html", "public/play/e8m0/c/index.html", "public/play/e8m0/rust/index.html", "public/play/e8m0/verilog/index.html", "public/play/e8m0/verilog-hir/index.html", "public/play/e8m0/js/index.html", "public/play/e8m0/ts/index.html"]; ; --- What the player takes from the site ------------------------------------------------------- ; The spec explorer's highlighter and palette, bundled from the site's own source rather than @@ -186,13 +186,13 @@ pub const SHELL_GEN_ALIAS : str = "t27c gen-{backend}"; ; --- Plays --------------------------------------------------------------------------------------- ; One entry per play, the same position in every array. SPEC is a path under public/t27/files/; ; CAST names a recording published at public/term//. -pub const PLAY_COUNT : u8 = 1; -pub const PLAY_IDS : [1]str = ["hello-world"]; -pub const PLAY_SPECS : [1]str = ["specs/demos/hello_world.t27"]; -pub const PLAY_CASTS : [1]str = ["t27c-hello-world"]; -pub const PLAY_TITLES : [1]str = ["hello_world.t27, compiled inside the post"]; -pub const PLAY_DESCRIPTIONS : [1]str = ["The t27 compiler, as WebAssembly, running on one spec in your browser: lexer to seven backends, the spec's own tests, the emitted code, its Verilog read for synthesis, and a terminal."]; -pub const PLAY_IMAGE_ALTS : [1]str = ["The t27 player: hello_world.t27 on the left, the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal."]; +pub const PLAY_COUNT : u8 = 2; +pub const PLAY_IDS : [2]str = ["hello-world", "e8m0"]; +pub const PLAY_SPECS : [2]str = ["specs/demos/hello_world.t27", "specs/numeric/e8m0.t27"]; +pub const PLAY_CASTS : [2]str = ["t27c-hello-world", "t27c-e8m0"]; +pub const PLAY_TITLES : [2]str = ["hello_world.t27, compiled inside the post", "e8m0.t27, the OCP MX scale byte, compiled inside the post"]; +pub const PLAY_DESCRIPTIONS : [2]str = ["The t27 compiler, as WebAssembly, running on one spec in your browser: lexer to seven backends, the spec's own tests, the emitted code, its Verilog read for synthesis, and a terminal.", "The OCP MX shared scale as a t27 spec, compiled by t27c as WebAssembly in your browser: one byte, 2^(e-127), code 255 is NaN. Seven backends, the spec's own tests and its Verilog."]; +pub const PLAY_IMAGE_ALTS : [2]str = ["The t27 player: hello_world.t27 on the left, the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal.", "The t27 player: e8m0.t27, the shared scale byte of the OCP Microscaling formats, on the left; the compiler's stages on the right with token, node and backend counts, and tabs for tests, code, RTL and a terminal."]; test the_player_is_sixteen_by_nine { assert PLAYER_WIDTH * 9 == PLAYER_HEIGHT * 16; @@ -267,11 +267,17 @@ test the_terminal_is_a_tab_and_gen_is_a_command { } test the_first_play_is_hello_world { - assert PLAY_COUNT == 1; assert PLAY_IDS[0] == "hello-world"; assert PLAY_SPECS[0] == "specs/demos/hello_world.t27"; } +test the_second_play_is_the_mx_scale_byte { + assert PLAY_COUNT == 2; + assert PLAY_IDS[1] == "e8m0"; + assert PLAY_SPECS[1] == "specs/numeric/e8m0.t27"; + assert PLAY_CASTS[1] == "t27c-e8m0"; +} + test the_terminal_reads_the_compile_stages { assert STAGES[0] == "source"; assert STAGES[5] == "backends"; diff --git a/apps/website/src/lib/course.generated.ts b/apps/website/src/lib/course.generated.ts index 44eae1e473..923e73ae16 100644 --- a/apps/website/src/lib/course.generated.ts +++ b/apps/website/src/lib/course.generated.ts @@ -1,5 +1,5 @@ // GENERATED by scripts/course-from-spec.mjs from specs/course/course.t27 and apps/website/i18n/course.ru.json -// spec sha256 6cf597cd89c4f7ee329aa136f777fbe2a2a49a92d4ad981d4ded48dcd2ce9ffb +// spec sha256 03695427d058cc14caeca0b5f58cd0061f240106dbde99035b8f760e599e5d6e // Do not edit: change the .t27 source (or the Russian bundle) and regenerate. export const COURSE = { @@ -8,7 +8,7 @@ export const COURSE = { "publicSpec": "public/learn/course.t27", "ruContract": "specs/course/course-ru.t27", "ruBundle": "apps/website/i18n/course.ru.json", - "sha256": "6cf597cd89c4f7ee329aa136f777fbe2a2a49a92d4ad981d4ded48dcd2ce9ffb", + "sha256": "03695427d058cc14caeca0b5f58cd0061f240106dbde99035b8f760e599e5d6e", "schemaVersion": 1, "gallery": "specs/widgets/gallery.t27" }, @@ -23,7 +23,7 @@ export const COURSE = { "say": { "en": { "TITLE": "From zero to a chip: FPGA programming with t27", - "DESCRIPTION": "27 lessons, one working widget each: what an FPGA is, how numbers become bits, your own t27 spec, how t27c compiles it, and how it becomes a bitstream on a real board. Then a lab of 3 on our own research.", + "DESCRIPTION": "27 lessons, one working widget each: what an FPGA is, how numbers become bits, your own t27 spec, how t27c compiles it, and how it becomes a bitstream on a real board. Then a lab of 3 on our own research, and 3 lessons on the MX number formats AI chips use.", "ALL": "All lessons", "ALSO": "Also try", "ALSO_HINT": "Each one swaps the widget above.", @@ -46,7 +46,7 @@ export const COURSE = { "PRIVATE": "Progress stays in this browser; nothing is sent.", "PROGRESS": "{0} of {1} done", "SEO_TITLE": "{0}: FPGA course, lesson {1} of {2}", - "SHAPE": "9 modules of 3 lessons, 27 cells, then a lab of 3. A filled cell is a lesson you marked done.", + "SHAPE": "9 modules of 3 lessons, 27 cells, then a lab of 3 and an AI numbers module of 3. A filled cell is a lesson you marked done.", "SHARE": "Link to share, with a preview card", "SOURCE": "This course is itself a t27 spec: read it", "SPEC": "Open the lesson's spec in the player", @@ -56,7 +56,7 @@ export const COURSE = { }, "ru": { "TITLE": "С нуля до чипа: программирование FPGA на t27", - "DESCRIPTION": "27 уроков, в каждом рабочий виджет: что такое FPGA, как числа становятся битами, ваша спека на t27, как её компилирует t27c и как она превращается в битстрим на настоящей плате. Затем лаборатория из 3 уроков о наших собственных исследованиях.", + "DESCRIPTION": "27 уроков, в каждом рабочий виджет: что такое FPGA, как числа становятся битами, ваша спека на t27, как её компилирует t27c и как она превращается в битстрим на настоящей плате. Затем лаборатория из 3 уроков о наших собственных исследованиях и 3 урока о форматах чисел MX, которыми пользуются ИИ-чипы.", "ALL": "Все уроки", "ALSO": "Ещё попробуйте", "ALSO_HINT": "Каждая кнопка меняет виджет выше.", @@ -79,7 +79,7 @@ export const COURSE = { "PRIVATE": "Прогресс хранится только в этом браузере; ничего не отправляется.", "PROGRESS": "Пройдено {0} из {1}", "SEO_TITLE": "{0}: курс по FPGA, урок {1} из {2}", - "SHAPE": "9 модулей по 3 урока, 27 клеток, затем лаборатория из 3. Закрашенная клетка — урок, который вы отметили пройденным.", + "SHAPE": "9 модулей по 3 урока, 27 клеток, затем лаборатория из 3 и модуль ИИ-чисел из 3. Закрашенная клетка — урок, который вы отметили пройденным.", "SHARE": "Ссылка, чтобы поделиться, с карточкой превью", "SOURCE": "Этот курс сам является спекой t27: прочитайте её", "SPEC": "Открыть спеку урока в плеере", @@ -258,6 +258,23 @@ export const COURSE = { "title": "Лаборатория: наши исследования", "line": "Свой формат чисел, честная таблица результатов и таблицы модели, перемноженные на плате." } + }, + { + "id": "mx", + "n": 11, + "lessons": [ + "one-scale-per-block", + "scale-byte-native", + "one-outlier" + ], + "en": { + "title": "AI numbers: the MX block", + "line": "How AI chips keep weights in a few bits: one shared scale per block, the scale byte itself, and what one outlier does to its neighbours." + }, + "ru": { + "title": "ИИ-числа: блок MX", + "line": "Как ИИ-чипы хранят веса в нескольких битах: один общий масштаб на блок, сам байт масштаба и что один выброс делает с соседями." + } } ], "lessons": [ @@ -1331,6 +1348,101 @@ export const COURSE = { "text": "Арифметику делает плата; компьютер отправляет ей работу через последовательный USB-мост. Ответы платы ждут в маленьком приёмном буфере микросхемы моста, и если держать в полёте больше запросов, чем помещается их ответов, ответы теряются. Окно поменьше убрало потери и не замедлило прогон. По этой линии плата перемножила все троичные матрицы весов небольшой языковой модели, и каждая строка совпала с эталоном бит в бит. Сам текст генерирует Mac, который делает всё, кроме троичных скалярных произведений: плата — это калькулятор, а не вся модель.", "task": "Тяните окно, пока ведро не переполнится, и запомните самое большое окно, которое помещается; затем сравните два настоящих прогона." } + }, + { + "id": "one-scale-per-block", + "n": 31, + "module": "mx", + "widget": { + "id": "play-e8m0", + "kind": "player", + "title": "e8m0.t27, the OCP MX scale byte, compiled inside the post", + "url": "https://t27.ai/play/e8m0/", + "preview": "play/embed.html?spec=specs%2Fnumeric%2Fe8m0.t27&cast=t27c-e8m0&v=ded547ceeb2f", + "height": 480 + }, + "also": [], + "spec": null, + "en": { + "title": "One scale for a block of weights", + "goal": "Why AI formats share one scale across a block of weights, and how the scale byte of OCP MX is read.", + "text": "A language model has millions of weights, and a full float for each is expensive to store and to move. The Microscaling (MX) formats of the Open Compute Project keep each weight in 4, 6 or 8 bits and give every block of 32 weights one shared scale. That scale is a single byte called E8M0: no sign and no mantissa, only an exponent, so code e means 2 to the power e minus 127, and code 255 means NaN. The player compiles e8m0.t27, the spec of that byte, in your browser and runs its tests. Where the browser cannot run a check, it skips it and says why.", + "task": "Run the tests and count the skips; then open the Verilog and find the function that checks for code 255." + }, + "ru": { + "title": "Один масштаб на блок весов", + "goal": "Почему ИИ-форматы делят один масштаб на блок весов и как читается байт масштаба OCP MX.", + "text": "У языковой модели миллионы весов, и хранить и пересылать полное число с плавающей точкой для каждого дорого. Форматы Microscaling (MX) консорциума Open Compute Project хранят каждый вес в 4, 6 или 8 битах и дают каждому блоку из 32 весов один общий масштаб. Этот масштаб — один байт, E8M0: без знака и без мантиссы, только порядок, так что код e означает 2 в степени e минус 127, а код 255 означает NaN. Плеер компилирует e8m0.t27, спеку этого байта, прямо в вашем браузере и запускает её тесты. Проверку, которую браузер выполнить не может, он пропускает и объясняет почему.", + "task": "Запустите тесты и посчитайте пропуски; затем откройте Verilog и найдите функцию, которая проверяет код 255." + } + }, + { + "id": "scale-byte-native", + "n": 32, + "module": "mx", + "widget": { + "id": "t27c-e8m0", + "kind": "cast", + "title": "t27c on e8m0.t27 -- the MX shared scale byte, native", + "url": "https://t27.ai/term/t27c-e8m0/", + "preview": "term/t27c-e8m0/index.html", + "height": 640 + }, + "also": [], + "spec": { + "path": "specs/numeric/e8m0.t27", + "source": "t27/files/specs/numeric/e8m0.t27", + "preview": "play/embed.html?spec=specs%2Fnumeric%2Fe8m0.t27&tab=term&v=ded547ceeb2f", + "height": 480 + }, + "en": { + "title": "The scale byte on a real machine", + "goal": "What the native compiler checks in the same spec, including what the browser skipped.", + "text": "The player in the last lesson could not run every check in e8m0.t27, and it said so. This recording runs the native t27c and Zig on a real machine on the same spec: its constants, the test report, the Zig tests, and the Verilog for the NaN check. Some checks are invariants the compiler proves while it compiles, so compiling is the check. Every byte in the recording was printed by the command; only the typing is staged.", + "task": "Find how many invariants the test report proved at compile time, and compare its pass count with the player's." + }, + "ru": { + "title": "Байт масштаба на настоящей машине", + "goal": "Что нативный компилятор проверяет в той же спеке, включая то, что браузер пропустил.", + "text": "Плеер в прошлом уроке не смог выполнить все проверки в e8m0.t27 и прямо об этом сказал. Эта запись запускает нативный t27c и Zig на настоящей машине на той же спеке: её константы, отчёт о тестах, тесты Zig и Verilog для проверки на NaN. Часть проверок — инварианты, которые компилятор доказывает во время компиляции, так что компиляция и есть проверка. Каждый байт в записи напечатала команда; инсценирован только набор текста.", + "task": "Найдите, сколько инвариантов отчёт доказал во время компиляции, и сравните число пройденных тестов с числом у плеера." + } + }, + { + "id": "one-outlier", + "n": 33, + "module": "mx", + "widget": { + "id": "t27c-ocp-mx", + "kind": "cast", + "title": "t27c on ocp_mx.t27 -- OCP MX v1.0, native", + "url": "https://t27.ai/term/t27c-ocp-mx/", + "preview": "term/t27c-ocp-mx/index.html", + "height": 640 + }, + "also": [ + { + "id": "t27-vs-nvfp4", + "kind": "tool", + "title": "T27 vs NVFP4: six weight formats on one small language model", + "url": "https://t27.ai/widgets/t27-vs-nvfp4/", + "preview": "widgets/t27-vs-nvfp4/index.html?embed=1", + "height": 640 + } + ], + "spec": null, + "en": { + "title": "One outlier, many zeros", + "goal": "What a shared scale costs: how one large weight pushes the small weights of its block to zero, format by format.", + "text": "A shared scale is set by the largest value in its block, so one outlier sets it for all 32 weights. Weights much smaller than the outlier then fall below the smallest step the element can show and are stored as zero. This recording runs ocp_mx.t27, our spec of OCP MX v1.0, natively on a block of 31 small weights and one outlier, and counts how many each element format flushes to zero. Then it plants a bug, one changed exponent bias, and exactly one test fails: a spec that carries its tests catches its own mistakes.", + "task": "Read which element format flushes the most small weights to zero and which two flush none; then find the test the planted bug breaks." + }, + "ru": { + "title": "Один выброс, много нулей", + "goal": "Чего стоит общий масштаб: как один крупный вес обнуляет мелкие веса своего блока, формат за форматом.", + "text": "Общий масштаб задаёт самое большое значение в блоке, поэтому один выброс задаёт его для всех 32 весов. Веса, намного меньшие выброса, падают ниже самого мелкого шага, который может показать элемент, и записываются нулём. Эта запись нативно запускает ocp_mx.t27, нашу спеку OCP MX v1.0, на блоке из 31 мелкого веса и одного выброса и считает, сколько весов обнуляет каждый формат элемента. Затем она подкладывает ошибку, одно изменённое смещение порядка, и падает ровно один тест: спека, которая несёт свои тесты, ловит собственные ошибки.", + "task": "Найдите, какой формат элемента обнуляет больше всего мелких весов, а какие два — ни одного; затем найдите тест, который ломает подложенная ошибка." + } } ] } as const diff --git a/apps/website/src/lib/queenWidgets.generated.ts b/apps/website/src/lib/queenWidgets.generated.ts index c436cdc45b..c7fc1174a5 100644 --- a/apps/website/src/lib/queenWidgets.generated.ts +++ b/apps/website/src/lib/queenWidgets.generated.ts @@ -1,12 +1,12 @@ // GENERATED by scripts/widgets-from-spec.mjs from specs/widgets/gallery.t27 -// spec sha256 0ede9d8e7eb66f7296ae6c3a7f87433930821d15929ac1a21e4c946a6eaf0673 +// spec sha256 8a448a89c134d72b546cd8020acc89c4f89d92ba34b56b487375965b4e94dfeb // Do not edit: change the .t27 source and regenerate. export const QUEEN_WIDGETS = { "source": { "spec": "specs/widgets/gallery.t27", "publicSpec": "public/widgets/gallery.t27", - "sha256": "0ede9d8e7eb66f7296ae6c3a7f87433930821d15929ac1a21e4c946a6eaf0673", + "sha256": "8a448a89c134d72b546cd8020acc89c4f89d92ba34b56b487375965b4e94dfeb", "schemaVersion": 1 }, "name": "t27 widgets", @@ -735,6 +735,72 @@ export const QUEEN_WIDGETS = { "iframe": "", "markdown": "[![Inside t27c: watch a compiler read your code, stage by stage](https://t27.ai/widgets/t27c-stages/card.png?v=9cab7cab1f0b)](https://t27.ai/widgets/t27c-stages/)" }, + { + "id": "t27c-e8m0", + "kind": "cast", + "category": "compiler", + "hook": "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", + "title": "t27c on e8m0.t27 -- the MX shared scale byte, native", + "line": "t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway): the E8M0 scale byte of OCP MX, 18 tests and 9 comptime invariants pass, and the Verilog backend's NaN check.", + "url": "https://t27.ai/term/t27c-e8m0/", + "view": null, + "image": "https://t27.ai/term/t27c-e8m0/card.png", + "gif": "https://t27.ai/term/t27c-e8m0/session.gif", + "preview": "term/t27c-e8m0/index.html", + "commands": [ + "git log -1 --format='%h %ad %s' --date=short -- specs/numeric/e8m0.t27 | cut -c1-100", + "sed -n 51,56p specs/numeric/e8m0.t27", + "t27c test-report specs/numeric/e8m0.t27 --specs-dir specs | sed -n '1,7p;$p'", + "t27c gen specs/numeric/e8m0.t27 > e8m0.zig && zig test e8m0.zig", + "t27c gen-verilog specs/numeric/e8m0.t27 | sed -n 69,78p" + ], + "recorded": "2026-10-06 14:11 UTC", + "iframe": "", + "markdown": "[![t27c on e8m0.t27 -- the MX shared scale byte, native](https://t27.ai/term/t27c-e8m0/session.gif)](https://t27.ai/term/t27c-e8m0/)" + }, + { + "id": "play-e8m0", + "kind": "player", + "category": "compiler", + "hook": "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", + "title": "e8m0.t27, the OCP MX scale byte, compiled inside the post", + "line": "The OCP MX shared scale as a t27 spec, compiled by t27c as WebAssembly in your browser: one byte, 2^(e-127), code 255 is NaN. Seven backends, the spec's own tests and its Verilog.", + "url": "https://t27.ai/play/e8m0/", + "view": null, + "image": "https://t27.ai/play/e8m0/card.png?v=4e1fe56048a2", + "gif": null, + "preview": "play/embed.html?spec=specs%2Fnumeric%2Fe8m0.t27&cast=t27c-e8m0&v=ded547ceeb2f", + "commands": [], + "recorded": null, + "iframe": "", + "markdown": "[![e8m0.t27, the OCP MX scale byte, compiled inside the post](https://t27.ai/play/e8m0/card.png?v=4e1fe56048a2)](https://t27.ai/play/e8m0/)" + }, + { + "id": "t27c-ocp-mx", + "kind": "cast", + "category": "compiler", + "hook": "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", + "title": "t27c on ocp_mx.t27 -- OCP MX v1.0, native", + "line": "t27c 0.4.0 and zig 0.16.0 on the t27c lab (Railway), spec at t27 1a3786657: 49 tests pass natively; one changed bias makes exactly one test fail; git restores the spec.", + "url": "https://t27.ai/term/t27c-ocp-mx/", + "view": null, + "image": "https://t27.ai/term/t27c-ocp-mx/card.png", + "gif": "https://t27.ai/term/t27c-ocp-mx/session.gif", + "preview": "term/t27c-ocp-mx/index.html", + "commands": [ + "git log -1 --format='%h %s' | cut -c1-100", + "t27c --version && zig version", + "sed -n 657,664p specs/numeric/ocp_mx.t27", + "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n '1,7p;$p'", + "t27c gen specs/numeric/ocp_mx.t27 > ocp_mx.zig && zig test ocp_mx.zig", + "sed -i 's/= \\[1, 1, 3, 7, 15, 0\\]/= [1, 1, 3, 7, 16, 0]/' specs/numeric/ocp_mx.t27 && git diff -U0 | tail -n 2", + "t27c test-report specs/numeric/ocp_mx.t27 --specs-dir specs | sed -n 1,8p", + "git checkout specs/numeric/ocp_mx.t27 && sha256sum specs/numeric/ocp_mx.t27" + ], + "recorded": "2026-10-06 14:11 UTC", + "iframe": "", + "markdown": "[![t27c on ocp_mx.t27 -- OCP MX v1.0, native](https://t27.ai/term/t27c-ocp-mx/session.gif)](https://t27.ai/term/t27c-ocp-mx/)" + }, { "id": "wars", "kind": "tab",