diff --git a/apps/website/src/components/SpecChipView.tsx b/apps/website/src/components/SpecChipView.tsx
new file mode 100644
index 0000000000..2b5f719c5d
--- /dev/null
+++ b/apps/website/src/components/SpecChipView.tsx
@@ -0,0 +1,386 @@
+// The CHIP layer: what a .t27 spec commits to in hardware, drawn and animated.
+//
+// WHAT THIS IS, precisely, because the distinction matters here more than the
+// picture does:
+//
+// This is derived from what the spec DECLARES -- the bit width of every constant,
+// the field layout of every packed struct, the parameter and return widths of
+// every function. Those are real hardware facts. hello_world.t27 says it in its
+// own comments: "a spec has to say what reaches hardware, not leave it to a
+// compiler default", and every field carries its own width for that reason.
+//
+// This is NOT a placed-and-routed netlist, and it is not synthesis output. The
+// .t27 -> Verilog backend currently emits module shells: across the 676-spec
+// corpus, 361 produce Verilog that yosys accepts, and every one of them yields
+// 0 LUTs and 0 flip-flops -- the 4-8 cells that appear are IBUF/OBUF pads. So
+// there is no cell placement to show, and drawing one would be an invention.
+//
+// What IS drawn is the datapath the declarations force: how many bit lanes each
+// value needs, where a struct's fields sit relative to one another, and which
+// stage feeds which. A reader learns what their types cost. That is worth
+// showing and is true; a fake floorplan would be neither.
+
+import { memo, useMemo, useState } from 'react'
+import type { ReactElement } from 'react'
+import type { T27Node } from '../lib/t27Compiler'
+import { SpecSiliconHistory } from './SpecSiliconHistory'
+
+const C = {
+ bg: '#0B0D0C',
+ panel: '#11150F',
+ wire: '#1d2a20',
+ green: '#00FF88',
+ gold: '#FFD700',
+ dim: '#8b9490',
+ ink: '#d7e0d8',
+ violet: '#c792ea',
+} as const
+
+const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace"
+
+/** Bit width of a t27 scalar type, or null when the type is not a fixed-width one. */
+export function widthOf(type: string | undefined): number | null {
+ if (!type) return null
+ const m = /^[iu](\d+)$/.exec(type.trim())
+ if (m) {
+ const n = Number(m[1])
+ return Number.isFinite(n) && n > 0 && n <= 512 ? n : null
+ }
+ if (/^bool$/.test(type.trim())) return 1
+ // A trit is three states. It does not fit in one bit and the language is built
+ // on that, so it is shown as 2 lanes rather than rounded down to 1.
+ if (/^trit$/i.test(type.trim())) return 2
+ return null
+}
+
+export interface ChipConst { name: string; type: string; width: number; value?: string }
+export interface ChipField { name: string; type: string; width: number }
+export interface ChipStruct { name: string; fields: ChipField[]; total: number }
+export interface ChipFn { name: string; params: ChipField[]; ret?: ChipField }
+
+export interface ChipModel {
+ module?: string
+ consts: ChipConst[]
+ structs: ChipStruct[]
+ fns: ChipFn[]
+ /** Widest single value in the design, for lane scaling. */
+ maxWidth: number
+ /** Declarations that carry no fixed width, so nothing honest can be drawn for them. */
+ unsized: number
+}
+
+/** Walk the AST and collect only what has a decidable hardware width. */
+export function buildChipModel(ast: T27Node | undefined): ChipModel {
+ const model: ChipModel = { consts: [], structs: [], fns: [], maxWidth: 1, unsized: 0 }
+ if (!ast) return model
+
+ const visit = (n: T27Node) => {
+ const kind = n.kind || ''
+
+ if (kind === 'Module' && n.name && !model.module) model.module = n.name
+
+ if (kind.startsWith('Const') && n.name) {
+ const w = widthOf(n.type)
+ // A struct constant is a layout, not a scalar -- handled below.
+ const isStruct = (n.children || []).some((c) => (c.kind || '').includes('Field'))
+ if (isStruct) {
+ const fields: ChipField[] = []
+ for (const c of n.children || []) {
+ const fw = widthOf(c.type)
+ const fname = c.field || c.name
+ if (fw && fname) fields.push({ name: fname, type: c.type || '', width: fw })
+ else if (fname) model.unsized += 1
+ }
+ if (fields.length) {
+ const total = fields.reduce((a, f) => a + f.width, 0)
+ model.structs.push({ name: n.name, fields, total })
+ model.maxWidth = Math.max(model.maxWidth, total)
+ }
+ } else if (w) {
+ model.consts.push({ name: n.name, type: n.type || '', width: w, value: n.value })
+ model.maxWidth = Math.max(model.maxWidth, w)
+ } else {
+ model.unsized += 1
+ }
+ }
+
+ if (kind.startsWith('Fn') && n.name) {
+ const params: ChipField[] = []
+ for (const p of n.params || []) {
+ const pw = widthOf(p.type)
+ if (pw) params.push({ name: p.name, type: p.type, width: pw })
+ else model.unsized += 1
+ }
+ const rw = widthOf(n.returnType)
+ const fn: ChipFn = { name: n.name, params }
+ if (rw) fn.ret = { name: 'out', type: n.returnType || '', width: rw }
+ if (params.length || fn.ret) {
+ model.fns.push(fn)
+ model.maxWidth = Math.max(model.maxWidth, ...params.map((p) => p.width), rw || 1)
+ }
+ }
+
+ for (const c of n.children || []) visit(c)
+ }
+
+ visit(ast)
+ return model
+}
+
+/** One bus, drawn as `width` parallel lanes. */
+function Bus({ x, y, w, bits, color, label, delay }: {
+ x: number; y: number; w: number; bits: number; color: string; label?: string; delay: number
+}) {
+ // Above ~16 lanes the individual wires stop being readable and start being
+ // texture, so the bus collapses to a band with the count written on it.
+ const drawn = Math.min(bits, 16)
+ const gap = 3
+ const h = drawn * gap
+ return (
+
+ {Array.from({ length: drawn }, (_, i) => (
+
+ ))}
+ {/* The travelling pulse: this is what makes it a clocked datapath rather
+ than a static diagram. One pulse per bus, staggered by stage. */}
+
+
+
+
+
+ {label && (
+
+ {label}
+
+ )}
+ {bits > drawn && (
+
+ {bits} lanes
+
+ )}
+
+ )
+}
+
+interface Props {
+ ast?: T27Node
+ specPath: string
+ /** Rendered verbatim; the caller owns wording and language. */
+ copy: {
+ title: string
+ derived: string
+ notSynth: string
+ empty: string
+ consts: string
+ structs: string
+ fns: string
+ bits: string
+ unsized: string
+ /** Contains {n}, replaced with the number of rows not drawn. */
+ omitted: string
+ }
+}
+
+function SpecChipViewImpl({ ast, specPath, copy }: Props) {
+ const model = useMemo(() => buildChipModel(ast), [ast])
+ const [running, setRunning] = useState(true)
+
+ const nothing = !model.consts.length && !model.structs.length && !model.fns.length
+
+ // Layout: one row per drawn element, stacked. Width is fixed; height grows.
+ //
+ // The row budget is not cosmetic. The SVG scales to the panel width, so a spec
+ // with 53 functions would shrink every label past legibility -- a diagram that
+ // shows everything and lets you read none of it. Draw a readable prefix and
+ // state the remainder in words instead of pretending to have drawn it.
+ const rowH = 74
+ const MAX_ROWS = 12
+ const constRow = model.consts.length ? 1 : 0
+ const budget = Math.max(1, MAX_ROWS - constRow)
+ const structsShown = model.structs.slice(0, budget)
+ const fnsShown = model.fns.slice(0, Math.max(0, budget - structsShown.length))
+ const omitted =
+ model.structs.length - structsShown.length + (model.fns.length - fnsShown.length)
+ const rows = constRow + structsShown.length + fnsShown.length
+ const H = Math.max(240, 90 + rows * rowH)
+ const W = 720
+
+ let y = 96
+ const nodes: ReactElement[] = []
+ let stage = 0
+
+ if (model.consts.length) {
+ const total = model.consts.reduce((a, c) => a + c.width, 0)
+ nodes.push(
+
+
+
+ {copy.consts}
+
+
+ {model.consts.length} × tie-off
+
+
+
+
+ constant ROM
+
+ ,
+ )
+ y += rowH
+ stage += 1
+ }
+
+ for (const s of structsShown) {
+ // A packed struct IS a bit layout, so it is drawn as one: fields side by
+ // side, each sized to its declared width. This is the most literally
+ // hardware-shaped thing a .t27 spec contains.
+ const scale = 380 / Math.max(s.total, 1)
+ let bx = 190
+ nodes.push(
+
+
+ {s.name}
+
+
+ packed · {s.total} {copy.bits}
+
+ {s.fields.map((f) => {
+ const w = Math.max(f.width * scale, 26)
+ const el = (
+
+
+ {f.name}
+ {f.type}
+
+ )
+ bx += w
+ return el
+ })}
+ ,
+ )
+ y += rowH
+ stage += 1
+ }
+
+ for (const fn of fnsShown) {
+ const inW = fn.params.reduce((a, p) => a + p.width, 0)
+ nodes.push(
+
+ {fn.params.length > 0 && (
+ p.type).join(' ')} delay={stage * 0.25} />
+ )}
+
+
+ {fn.name}
+
+
+ {fn.params.length} in → {fn.ret ? 1 : 0} out
+
+ {fn.ret && (
+
+ )}
+ {fn.ret && (
+
+ )}
+ ,
+ )
+ y += rowH
+ stage += 1
+ }
+
+ return (
+
+
+ {/* What the schematic above declares, set against what has actually been
+ on the board. Renders a plain "no hardware run" for most specs. */}
+
+
+
+
+ )
+}
+
+export const SpecChipView = memo(SpecChipViewImpl)
diff --git a/apps/website/src/components/SpecSiliconHistory.tsx b/apps/website/src/components/SpecSiliconHistory.tsx
new file mode 100644
index 0000000000..ce1ebb10d5
--- /dev/null
+++ b/apps/website/src/components/SpecSiliconHistory.tsx
@@ -0,0 +1,267 @@
+// The hardware record for a spec's format: what has been synthesized, what has
+// been proven, and what has actually run on the board.
+//
+// The hard rule this file exists to enforce: a claim is shown at the strength it
+// was earned, and the difference between "this spec declares it", "a SAT engine
+// proved it", "a bitstream exists" and "a board returned the right bits over a
+// wire" is never smoothed over. Formats with no hardware run say so plainly
+// instead of rendering an empty timeline that reads like a pending one.
+
+import { memo } from 'react'
+import { useI18n } from '../i18n/context'
+import {
+ CHAIN, DEVICE, FAMILY_SPECS, FAMILY_TOTALS, FORMAL_PROVEN, SPEC_TO_FORMAT,
+ TOOLCHAIN, siliconFor,
+} from '../data/siliconHistory'
+import type { Level, OpKind, SiliconCell } from '../data/siliconHistory'
+
+const C = {
+ bg: '#0B0D0C',
+ panel: '#11150F',
+ wire: '#1d2a20',
+ green: '#00FF88',
+ gold: '#FFD700',
+ amber: '#a87a4a',
+ dim: '#8b9490',
+ ink: '#d7e0d8',
+} as const
+
+const MONO = "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace"
+
+const T = {
+ en: {
+ title: 'HARDWARE HISTORY',
+ none: 'No hardware run. Nothing generated from this spec has been synthesized, flashed or measured — and the Verilog the compiler emits for it is a module shell, so there is nothing yet to place.',
+ familyIntro: 'This spec describes the catalog rather than one format. Catalog-wide totals:',
+ onSilicon: 'measured on silicon',
+ ofCatalog: 'of 83 catalog formats',
+ decodeHw: 'decode, on silicon',
+ addHw: 'ADD, on silicon',
+ mulHw: 'MUL, on silicon',
+ swOnly: 'software bit-exact',
+ structural: 'structural by design — not convertible without a format decision',
+ chainTitle: 'Evidence chain — a cell counts only with all four',
+ device: 'Device',
+ toolchain: 'Toolchain',
+ oracle: 'Golden oracle',
+ ops: 'Operations',
+ caught: 'What the hardware caught',
+ levels: {
+ silicon: 'on silicon',
+ prepared: 'bitstream ready, not yet flashed',
+ formal: 'proven by SAT over all inputs',
+ declared: 'declared only',
+ } as Record,
+ formalNote: 'Proven by SAT over the whole input space, against an oracle written independently of the design:',
+ disclaimer:
+ 'These results are for hand-written RTL of the same format, not for the Verilog this spec compiles to. The spec and the core describe the same number format; they are not the same artifact. Every figure is transcribed from the published evidence chain on EPIC #199 — CI run, bitstream SHA-256, JTAG flash, UART log.',
+ },
+ ru: {
+ title: 'ИСТОРИЯ НА ЖЕЛЕЗЕ',
+ none: 'Запусков на железе нет. Ничего сгенерированного из этой спеки не синтезировалось, не прошивалось и не измерялось, а Verilog, который компилятор для неё выдаёт, — это оболочка модуля, размещать пока нечего.',
+ familyIntro: 'Эта спека описывает каталог, а не один формат. Итоги по каталогу:',
+ onSilicon: 'измерено на кристалле',
+ ofCatalog: 'из 83 форматов каталога',
+ decodeHw: 'декодирование, на кристалле',
+ addHw: 'ADD, на кристалле',
+ mulHw: 'MUL, на кристалле',
+ swOnly: 'битточно программно',
+ structural: 'структурные по замыслу — без решения по формату не переводятся',
+ chainTitle: 'Цепочка доказательств — ячейка засчитывается только при всех четырёх',
+ device: 'Плата',
+ toolchain: 'Инструменты',
+ oracle: 'Эталон',
+ ops: 'Операции',
+ caught: 'Что поймало железо',
+ levels: {
+ silicon: 'на кристалле',
+ prepared: 'битстрим готов, не прошит',
+ formal: 'доказано SAT на всех входах',
+ declared: 'только объявлено',
+ } as Record,
+ formalNote: 'Доказано SAT на всём пространстве входов против эталона, написанного независимо от схемы:',
+ disclaimer:
+ 'Эти результаты относятся к написанному вручную RTL того же формата, а не к Verilog, в который компилируется эта спека. Спека и ядро описывают один и тот же числовой формат, но это разные артефакты. Все цифры перенесены из опубликованной цепочки доказательств в EPIC #199 — прогон CI, SHA-256 битстрима, прошивка по JTAG, лог UART.',
+ },
+}
+
+const LEVEL_COLOR: Record = {
+ silicon: C.green,
+ prepared: C.gold,
+ formal: '#c792ea',
+ declared: C.dim,
+}
+
+function Row({ label, value }: { label: string; value: string }) {
+ return (
+
+ )
+
+ // Nothing has been through the board for this spec. Say it, rather than
+ // rendering an empty frame that reads as "in progress".
+ if (!record && !isFamily) {
+ return (
+
+ )
+}
+
+export const SpecSiliconHistory = memo(SpecSiliconHistoryImpl)
diff --git a/apps/website/src/data/siliconHistory.ts b/apps/website/src/data/siliconHistory.ts
new file mode 100644
index 0000000000..5b5e530e82
--- /dev/null
+++ b/apps/website/src/data/siliconHistory.ts
@@ -0,0 +1,176 @@
+// Hardware and silicon history for spec formats that have been through the board.
+//
+// PROVENANCE — read this before adding a row.
+//
+// Every figure below is transcribed from
+// research/goldenfloat-hw-conformance/GOLDENFLOAT_HW_CONFORMANCE_v0.2.md
+// in gHashTag/trinity-fpga, whose own source is the evidence chain published on
+// EPIC #199. A cell is "Tier E" only when all four links exist and are public:
+// CI run id -> bitstream SHA-256 -> JTAG flash -> UART log. Tier C (self-report)
+// is not represented here at all, because there is none left.
+//
+// What this data is NOT: it is not a synthesis report for the Verilog that the
+// .t27 compiler emits from these specs. That Verilog is a module shell -- across
+// the corpus it yields 0 LUTs and 0 flip-flops. The silicon results belong to
+// hand-written RTL in external/tt-trinity-corona and fpga/openxc7-synth, verified
+// against an oracle written independently of it. The spec and the RTL describe the
+// same format; they are not the same artifact, and the UI must say so.
+
+export type OpKind = 'ADD' | 'MUL' | 'SUB' | 'decode'
+
+/** How far a claim has actually been carried. Ordered weakest to strongest. */
+export type Level = 'declared' | 'formal' | 'prepared' | 'silicon'
+
+export interface SiliconCell {
+ op: OpKind
+ level: Level
+ /** Exhaustive-or-sampled code coverage, exactly as published, e.g. "512/512". */
+ codes?: string
+ /** One line on what this particular cell cost or taught. */
+ note?: { en: string; ru: string }
+}
+
+export interface SiliconRecord {
+ /** Catalog format key, e.g. "gf16". */
+ format: string
+ cells: SiliconCell[]
+ /** A defect this format's hardware run exposed that simulation had missed. */
+ caseStudy?: { en: string; ru: string }
+}
+
+/** The board every one of these numbers was measured on. */
+export const DEVICE = {
+ board: 'ALINX AX7203',
+ part: 'xc7a200tfbg484-2',
+ idcode: '0x13636093',
+ clock: 'CFGMCLK via STARTUPE2, ~69-70 MHz measured',
+ uart: 'CP2102N @ 160000 baud',
+} as const
+
+/** The open toolchain. No vendor licence is required at any step. */
+export const TOOLCHAIN = {
+ synth: 'yosys',
+ pnr: 'nextpnr-xilinx',
+ db: 'Project X-Ray',
+ image: 'regymm/openxc7:latest',
+ flash: 'openocd (AL321 / FT2232H)',
+ oracle: 'conformance/gf_ref.py — exact rational arithmetic (fractions.Fraction)',
+} as const
+
+/** The four links. A cell is Tier E only when it has all of them. */
+export const CHAIN: { id: string; en: string; ru: string }[] = [
+ { id: 'synth', en: 'openXC7 CI synth', ru: 'синтез в CI (openXC7)' },
+ { id: 'bit', en: 'bitstream + SHA-256', ru: 'битстрим + SHA-256' },
+ { id: 'flash', en: 'JTAG flash', ru: 'прошивка по JTAG' },
+ { id: 'uart', en: 'UART verify vs golden', ru: 'проверка по UART против эталона' },
+]
+
+const RECORDS: SiliconRecord[] = [
+ {
+ format: 'gf4',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '256/256', note: { en: 'BIAS=0 fix', ru: 'исправление BIAS=0' } },
+ { op: 'MUL', level: 'silicon', codes: '256/256' },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ },
+ {
+ format: 'gf8',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '512/512' },
+ { op: 'MUL', level: 'silicon', codes: '480/480' },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ },
+ {
+ format: 'gf12',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '512/512' },
+ { op: 'MUL', level: 'silicon', codes: '480/480' },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ },
+ {
+ format: 'gf16',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '512/512', note: { en: 'NaN fix', ru: 'исправление NaN' } },
+ { op: 'MUL', level: 'silicon', codes: '512/512' },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ caseStudy: {
+ en: 'GF16 is the only width with HAS_INF=1. The adder returned Inf where NaN was required — and the reference testbench had the same blind spot, so simulation reported 30000/30000 PASS three times running. Only the independently written golden, run against the board, found the 6 failures in 512. Fixed, then 512/512 on silicon.',
+ ru: 'GF16 — единственная ширина с HAS_INF=1. Сумматор возвращал Inf там, где требовался NaN, и у эталонного стенда была та же слепая зона: симуляция трижды показала 30000/30000 PASS. Ошибку (6 из 512) нашёл только независимо написанный эталон, запущенный против платы. После исправления — 512/512 на кристалле.',
+ },
+ },
+ {
+ format: 'gf20',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '480/480', note: { en: 'placer fix', ru: 'исправление размещения' } },
+ { op: 'MUL', level: 'silicon', codes: '480/480' },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ caseStudy: {
+ en: 'The GF20 build was cancelled nine times under the wrong diagnosis "Docker Hub pull hang". Per-step CI timing showed the pull took about a minute. The real blocker was place-and-route: the simulated-annealing placer failed to route the wider netlist in 40 minutes; the analytical placer (--placer heap) routed it in about 8 seconds.',
+ ru: 'Сборку GF20 отменяли девять раз с неверным диагнозом «зависание docker pull». Потактовый разбор времени в CI показал, что pull занимает около минуты. Настоящей причиной была трассировка: размещение отжигом не развело более широкую схему за 40 минут, аналитический размещатель (--placer heap) справился примерно за 8 секунд.',
+ },
+ },
+ {
+ format: 'gf24',
+ cells: [
+ { op: 'ADD', level: 'silicon', codes: '480/480' },
+ { op: 'MUL', level: 'silicon', codes: '480/480', note: { en: 'needs synth_xilinx -nodsp', ru: 'требует synth_xilinx -nodsp' } },
+ { op: 'SUB', level: 'prepared' },
+ ],
+ },
+]
+
+export const SILICON: Record = Object.fromEntries(
+ RECORDS.map((r) => [r.format, r]),
+)
+
+/**
+ * Spec path -> catalog format. Written out rather than inferred from the filename,
+ * so a spec never picks up a hardware claim by resembling one.
+ */
+export const SPEC_TO_FORMAT: Record = {
+ 'specs/numeric/gf4.t27': 'gf4',
+ 'specs/numeric/gf8.t27': 'gf8',
+ 'specs/numeric/gf12.t27': 'gf12',
+ 'specs/numeric/gf16.t27': 'gf16',
+ 'specs/numeric/gf20.t27': 'gf20',
+ 'specs/numeric/gf24.t27': 'gf24',
+}
+
+/**
+ * Specs that describe the catalog rather than one format. They get the family
+ * totals instead of a single format's cells.
+ */
+export const FAMILY_SPECS = new Set([
+ 'specs/numeric/goldenfloat_family.t27',
+ 'specs/numeric/formats.t27',
+ 'specs/numeric/gf_competitive.t27',
+])
+
+/** Catalog-wide totals, as published in the v0.2 draft. */
+export const FAMILY_TOTALS = {
+ tierE: 27,
+ catalog: 83,
+ decodeHw: 13,
+ addHw: 7,
+ mulHw: 7,
+ swBitexact: 62,
+ structural: 15,
+}
+
+/** The formal track: which widths a SAT engine has proven over their whole input space. */
+export const FORMAL_PROVEN: Record = {
+ gf4: ['ADD', 'MUL'],
+ gf6: ['ADD', 'MUL'],
+ gf8: ['ADD'],
+ gf12: ['ADD'],
+}
+
+export function siliconFor(specPath: string): SiliconRecord | null {
+ const key = SPEC_TO_FORMAT[specPath]
+ return key ? SILICON[key] ?? null : null
+}
diff --git a/apps/website/src/pages/SpecExplorer.tsx b/apps/website/src/pages/SpecExplorer.tsx
index 4b2a1a4dbd..de324264f4 100644
--- a/apps/website/src/pages/SpecExplorer.tsx
+++ b/apps/website/src/pages/SpecExplorer.tsx
@@ -16,6 +16,7 @@ import { Link } from 'react-router-dom'
import { useI18n } from '../i18n/context'
import { usePageMeta } from '../hooks/usePageMeta'
import { SpecCodeView } from '../components/SpecCodeView'
+import { SpecChipView } from '../components/SpecChipView'
import { SpecEditor } from '../components/SpecEditor'
import { SpecMetrics } from '../components/SpecMetrics'
import { SpecShare } from '../components/SpecShare'
@@ -53,6 +54,20 @@ const UI = {
loading: 'Loading compiler…',
compiling: 'Analysing…',
back: '← Home',
+ chipTitle: 'ON THE CHIP',
+ chipDerived:
+ 'Derived from what this spec declares: the bit width of every constant, the field layout of every packed struct, and the parameter and return widths of every function. Those are hardware facts the language requires you to state.',
+ chipNotSynth:
+ 'Not a placed-and-routed netlist. The .t27 → Verilog backend currently emits module shells: of 676 specs, 361 produce Verilog yosys accepts, and every one yields 0 LUTs and 0 flip-flops. There is no cell placement to show, so none is drawn.',
+ chipEmpty:
+ 'This spec declares nothing with a fixed bit width, so there is no datapath to draw. Nothing is inferred to fill the space.',
+ chipConsts: 'constants',
+ chipStructs: 'packed structs',
+ chipFns: 'functions',
+ chipBits: 'bits',
+ chipUnsized: 'declaration(s) carry no fixed width and are not drawn.',
+ chipOmitted:
+ '{n} further declaration(s) are not drawn. The diagram scales to the panel, so past about a dozen rows every label stops being readable; the remainder is counted here rather than rendered too small to read.',
source: 'Source',
tokens: 'Tokens',
ast: 'AST',
@@ -134,6 +149,20 @@ const UI = {
loading: 'Загрузка компилятора…',
compiling: 'Анализ…',
back: '← На главную',
+ chipTitle: 'НА КРИСТАЛЛЕ',
+ chipDerived:
+ 'Построено из того, что объявляет спека: разрядность каждой константы, раскладка полей каждой packed-структуры, ширины параметров и возврата каждой функции. Это аппаратные факты, которые язык требует указать явно.',
+ chipNotSynth:
+ 'Это не размещённый и не разведённый нетлист. Бэкенд .t27 → Verilog сейчас выдаёт оболочки модулей: из 676 спек 361 даёт Verilog, который принимает yosys, и каждая — 0 LUT и 0 триггеров. Размещать нечего, поэтому ничего и не нарисовано.',
+ chipEmpty:
+ 'Эта спека не объявляет ничего с фиксированной разрядностью, поэтому тракт данных рисовать не из чего. Ничего не додумано.',
+ chipConsts: 'константы',
+ chipStructs: 'packed-структуры',
+ chipFns: 'функции',
+ chipBits: 'бит',
+ chipUnsized: 'объявлений без фиксированной разрядности не нарисованы.',
+ chipOmitted:
+ 'Ещё {n} объявлений не нарисованы. Схема масштабируется под панель, и после десятка строк подписи перестают читаться; остаток посчитан здесь, а не отрисован нечитаемо мелко.',
source: 'Исходник',
tokens: 'Токены',
ast: 'AST',
@@ -250,6 +279,7 @@ const LAYERS = [
{ id: 'verilog_hir', kind: 'target' },
{ id: 'c', kind: 'target' },
{ id: 'rust', kind: 'target' },
+ { id: 'chip', kind: 'chip' },
] as const
type LayerId = (typeof LAYERS)[number]['id']
@@ -265,6 +295,7 @@ const LAYER_LABEL: Record = {
verilog_hir: 'Verilog (HIR)',
c: 'C',
rust: 'Rust',
+ chip: 'Chip',
}
// ---------------------------------------------------------------- AST tree
@@ -1536,6 +1567,26 @@ export default function SpecExplorer() {
)
)}
+ {/* on-the-chip schematic, derived from declared widths */}
+ {layer === 'chip' && result && (
+
+ )}
+
{/* codegen targets */}
{LAYERS.find((l) => l.id === layer)?.kind === 'target' && result && (
!activeTarget ? (