Skip to content

DECIMA-8 Resonance Swarm Contract v0.2 (Activation Graph + Range Fuse + Decay-to-Zero)

Codename: Siberian Tank Interface

Status: v0.2 DESIGN FREEZE

Owner: Conductor (Digital Island) ↔ Swarm/Island (Analog Core)


0) Scope / Non-Goals

Scope (v0.2)

  • Один детерминированный ритм для: Emulator → Proto (PCB) → FPGA → ASIC.
  • Level16: 0..15 на каждой из 8 линий данных.
  • Двунаправленный обмен на VSB:
  • Conductor задаёт входной вектор до READ и держит стабильно на апертуре READ,
  • Island драйвит VSB только в WRITE (readout после WRITE).
  • Tile = минимальная программируемая сущность (RuleROM адресует тайлы).
  • Все данные передаются только через общую шину BUS16 (8 lane). Соседи данные не передают.
  • Соседи формируют граф активации (эстафету): locked-тайл активирует потомков для чтения BUS.
  • Фьюз (LOCK) по диапазону: тайл защёлкивается только если thr_cur16 ∈ [thr_lo16..thr_hi16] (оба границы — i16).
  • Decay тянет к 0: если decay16>0, аккумулятор каждый tick уменьшается по модулю к нулю и никогда не перескакивает через 0.
  • Схлоп ветки: если тайл становится неактивным (не ACTIVE), он принудительно сбрасывается в thr_cur16=0, locked=0, и не участвует в вычислениях/драйве.
  • Bake применяется атомарно только между EV_FLASH.
  • MT не вводится: один swarm = один поток/процесс.

Non-Goals (v0.2)

  • Абсолютные вольтажи не фиксируются; фиксируется семантика Level16 + ритм.
  • Никаких изменений baked-параметров внутри EV_FLASH.
  • Совместимость со старым форматом v0.1 не требуется.

1) Термины

  • Conductor — цифровой дирижёр (CPU), готовит вход, дергает EV_FLASH, делает bake/reset.
  • Island/Swarm — сеть тайлов + общая шина BUS16 (8 lane) поверх VSB.
  • Tile — узел ткани: 8 входных lane, 8 выходных lane (в BUS), локальный FUSE (thr/lock), веса и routing_flags16.
  • Level16 — значение 0..15 (4 бита), семантика “уровня/энергии”.
  • READ phase — тайлы читают вход (VSB_INGRESS + опц. BUS16), обновляют runtime, НЕ драйвят BUS.
  • WRITE phase — Conductor перестаёт драйвить шину, Island драйвит BUS16; пишут тайлы с BUS_W и locked-предком (или locked self), вход не читается.
  • Domain — 0..15; группа тайлов для сброса thr_cur16/locked.
  • RESET_DOMAIN(mask16) — сброс доменов (через EV_RESET_DOMAIN или авто-reset).
  • BAKE_APPLIED — bool; 0 после power-on/reset, становится 1 после успешного EV_BAKE().
  • RoutingFlags16 — 16-битное поле флагов маршрутизации/активации (см. раздел 8).
  • Parents(t) — множество родительских тайлов, имеющих ребро активации в сторону t (см. раздел 8).
  • ACTIVE(t) — тайл “в цепочке живой”: может читать BUS16, вычисляться, применять decay.

2) Hard Constants (заморозка v0.2)

  • VSB: 8 линий данных VSB[0..7], каждая несёт Level16.
  • BUS16: 8 lane, суммирование вкладов в WRITE.
  • Domains: ровно 16 доменов (0..15).
  • Дисциплина фаз: READ/WRITE обязательны; тайл не может одновременно читать и писать в одной фазе.
  • Routing: на тайл есть RoutingFlags16 (10 используемых бит):

N, E, S, W, NE, SE, SW, NW, BUS_R, BUS_W — по 1 биту каждый. - Всегда работаем по всем 8 lane. Нет per-lane масок. - Данные соседям не отправляются. Соседи только формируют граф активации для разрешения чтения BUS.


3) Плоскости интерфейса

3.1 Data Plane: VSB[0..7]

Каждая линия несёт Level16; данные от предыдущего тика доступны только через граф активации (BUS_R), не через арифметическое суммирование

3.2 Rhythm Plane: READ/WRITE (двухфазный предохранитель)

  • READ: все тайлы семплируют вход и обновляют state.
  • WRITE: тайлы выставляют выходы в BUS16.
  • Между ними обязателен turnaround (зазор направления), чтобы Conductor отпустил VSB и Island мог её драйвить.
  • Conductor читает BUS16 только после завершения WRITE.

3.3 Config Plane: CFG (SPI-like)

CFG_CS, CFG_SCLK, CFG_MOSI, CFG_MISO

Через CFG:

  • загрузка BakeBlob в staging
  • чтение FLAGS
  • команда EV_RESET_DOMAIN(mask16)
  • (опционально) чтение BAKE_ID_ACTIVE / PROFILE_ID_ACTIVE

4) Event Protocol / SHM ABI

4.1 Внешние события (API)

  • EV_FLASH(tag_u32)

    • выполняет один детерминированный цикл READ→WRITE
    • возвращает readout (R0/R1), FLAGS читаются отдельно (CFG) или в return struct
    • разрешён только если BAKE_APPLIED==1, иначе NotBaked (состояние не меняется)
  • EV_RESET_DOMAIN(mask16)

    • только между EV_FLASH
    • только если BAKE_APPLIED==1, иначе NotBaked
  • EV_BAKE()

    • только между EV_FLASH
    • применяет staging BakeBlob атомарно
    • при успехе делает reset runtime (см. 6.3)
    • при ошибке ничего не меняет

4.2 Внутренние подфазы EV_FLASH (не внешний API)

  1. PHASE_READ
  2. TURNAROUND
  3. PHASE_WRITE
  4. READOUT_SAMPLE
  5. INTERPHASE_AUTORESET (опционально, по разделу 15)

4.3 Readout timing

Default R0_RAW_BUS:

  • Conductor читает BUS16[0..7] сразу после завершения PHASE_WRITE этого же EV_FLASH.
  • В SHM: EV_FLASH заполняет OUT_buf, Conductor читает после возврата.

4.4 Bake Transaction / CFG staging

Staging buffer в Digital Island. EV_BAKE применяет параметры в fabric.

Ошибки EV_BAKE (минимум):

OK, BakeBadMagic, BakeBadVersion, BakeBadLen, BakeMissingTLV, BakeBadTLVLen, BakeCRCFail, BakeReservedNonZero, TopologyMismatch, BakeNoBlob.


5) Канонический Tick (одна “вспышка”)

5.1 Setup (Conductor)

  • Conductor выставляет VSB_INGRESS16[0..7] (Level16).
  • Держит стабильным до конца апертуры READ.

5.2 PHASE_READ (Island)

В начале READ:

  • фиксируем locked_before[t] = locked[t] для всех тайлов
  • вычисляем ACTIVE[t] как замыкание по графу активации (см. 6.1)
  • если ACTIVE[t]==0 → тайл принудительно в ноль (см. 6.1), не вычисляется и не драйвит

Для ACTIVE[t]==1:

  • формируем in16 (VSB_INGRESS) по 6.1
  • если locked_before==0: считаем row-pipeline (6.5), обновляем thr_cur16 с decay (6.6), определяем locked_after
  • если locked_before==1: тайл остаётся locked, матрица/decay не применяются
  • выбираем drive_vec (6.8)

5.3 TURNAROUND

  • Conductor снимает драйв VSB (Hi-Z / no-drive).
  • Island включает драйв только в WRITE.

5.4 PHASE_WRITE (Island)

  • Тайл пишет в BUS16 только если BUS_W==1 и (locked self или есть locked-предок).
  • Пишется весь drive_vec[0..7] (все 8 lane), дальше “честное суммирование” (раздел 13).

5.5 READOUT_SAMPLE (Conductor)

  • R0_RAW_BUS: readout = BUS16[0..7] как 8×Level16.

5.6 INTERPHASE_AUTORESET (опционально)

  • после фиксации readout и FLAGS32_LAST применяем AutoReset-by-Fire (раздел 15).

6) Tile Model v0.2

6.1 ACTIVE + вход тайла (эстафета + мгновенный схлоп ветки)

Routing edges: направления N/E/S/W/NE/SE/SW/NW формируют ребро A→B если у A стоит флаг и сосед B существует (см. 8).

Parents(t): все p, у которых есть ребро p→t.

ACTIVE closure (канон): Seed: ACTIVE[t]=1 если BUS_R==1 (источник/корень цепочки). Propagate: ACTIVE[t]=1 если существует p∈Parents(t) такой, что ACTIVE[p]==1 и locked_before[p]==1. Это вычисляется как монотонное замыкание до стабилизации (least fixed point). Детерминизм обеспечен тем, что используется только locked_before.

Вход тайла и принудительный ноль:

Если ACTIVE[t]==0, то тайл считается «мёртвым участком»:

thr_cur16 := 0 locked := 0 drive_vec := {0..0}

веса/row/decay не применяются в этом tick

Если ACTIVE[t]==1, то тайл читает только VSB_INGRESS16 (все 8 lane):

for i in 0..7:
  in16[t][i] = clamp15(VSB_INGRESS16[i])
  IN_CLIP[t][i] = (VSB_INGRESS16[i] > 15)

Важно: Шина BUS16 отражает состояние, сформированное в предыдущем EV_FLASH, и не суммируется с VSB в текущем тике. Единственная роль BUS16 в фазе READ — семантическая: тайлы с флагом BUS_R становятся источниками графа активации (ACTIVE seed). Данные из шины не участвуют в вычислении in16.

Эстафета: Сигнал распространяется от предка к потомку за 2 тика:

  • тик N: предок фьюзится → драйвит шину в PHASE_WRITE
  • тик N+1: потомок активируется через BUS_R → читает VSB_INGRESS → вычисляется

6.2 Baked state тайла (v0.2)

Baked:

  • thr_lo16 (i16)
  • thr_hi16 (i16) (инвариант: thr_lo16 <= thr_hi16)
  • decay16 (u16) (0..32767)
  • domain_id4 (0..15)
  • priority8 (0..255)
  • pattern_id16 (0..32767)
  • routing_flags16 (u16) (см. 8)
  • W[8][8] — SignedWeight5 (mag3∈[0..7]+sign1)
  • reset_on_fire_mask16 (u16)

Runtime:

  • thr_cur16 (i16: -32768..+32767)
  • locked (0/1)

6.3 Reset semantics (между вспышками)

EV_RESET_DOMAIN(mask16) применяется только между EV_FLASH.

Авто-reset применяется в INTERPHASE_AUTORESET.

Если домен тайла попадает в mask16:

  • thr_cur16 := 0
  • locked := 0

Эстафета: если корневой/промежуточный тайл расфюжен (reset/collide), то в следующем EV_FLASH он не даст locked_before==1, и downstream станет ACTIVE==0 → принудительно в 0.


6.4 Канонические функции (SignedWeight5)

Вес: mag3∈[0..7], sign1∈{0,1} (1="+", 0="−").

Каноническое signed-умножение (без деления):

mul_signed_raw(a, mag, sign) = (sign ? +1 : -1) * (a * mag) где a∈[0..15], mag∈[0..7] → [-105..+105] за один член. 8 членов в строке → [-840..+840] за строку.

clamp15(x)=clamp_range(x,0,15).


6.5 RowOut pipeline в PHASE_READ (для ACTIVE && !locked_before)

Два независимых вычисления:

  1. Для аккумулятора (delta): матричное умножение входа на веса
  2. Для шины (drive): масштабирование входа на сумму весов строки

Для каждой строки r=0..7:

/* Матричное умножение для аккумулятора */
row_raw_signed[r] = Σ_{i=0..7} (in16[i] * Wmag[r][i] * sign)  → [-840..+840]

/* Сумма весов строки для шины */
weight_sum[r] = Σ_{i=0..7} (Wmag[r][i] * sign)  → [-56..+56]

/* Масштабирование для шины */
row16_out[r] = clamp15( (max(in16[r] * weight_sum[r], 0) + 28) / 56 ) → 0..15

Нормализация для шины:

  • weight_sum = +56 (все веса +7) → 100% пропуск (row16_out = in16[r])
  • weight_sum = +28 (половина весов) → ~50% пропуск (row16_out ≈ in16[r] / 2)
  • weight_sum = 0 или отрицательная → 0% (тишина)

Для аккумулятора (signed вклад, без clamp):

row16_signed[r] = row_raw_signed[r] → [-840..+840]

6.6 Аккумулятор + decay-to-zero + fuse-by-range

locked_before = locked (снимок в начале READ).

Правила применяются только если ACTIVE==1.

Decay применяется всегда (если decay16>0), даже на locked тайлах.

Если locked_before==0:

  1. delta_raw = Σ_{r=0..7} row16_signed[r] → [-6720..+6720]
  2. thr_tmp = thr_cur16 + delta_raw (в i32)
  3. Decay тянет к 0 и не перескакивает 0:
if (decay16 > 0) {
  if (thr_tmp > 0) thr_tmp = max(thr_tmp - decay16, 0)
  else if (thr_tmp < 0) thr_tmp = min(thr_tmp + decay16, 0)
}
thr_cur16 = (i16)clamp_range(thr_tmp, -32768, 32767)
  1. Фьюз по диапазону:

range_active = (thr_lo16 < thr_hi16) in_range = range_active && (thr_lo16 <= thr_cur16) && (thr_cur16 <= thr_hi16)

has_signal = (delta_raw != 0) entered_by_decay = (decay16 > 0) && (in_range == true) && (in_range_before_decay == false) locked_after = (BAKE_APPLIED==1) && in_range && (has_signal || entered_by_decay)

Примечание: thr_lo16 == thr_hi16 означает отключенный фьюз (в т.ч. базовый случай 0..0) — такой тайл не защёлкивается.

Если locked_before==1:

  • Decay применяется (если decay16>0, thr_cur16 тянется к 0):
if (decay16 > 0) {
  if (thr_cur16 > 0) thr_cur16 = max(thr_cur16 - decay16, 0)
  else if (thr_cur16 < 0) thr_cur16 = min(thr_cur16 + decay16, 0)
}
  • Проверка на разлочивание: после decay проверяем диапазон:
in_range = (thr_lo16 < thr_hi16) && (thr_lo16 <= thr_cur16) && (thr_cur16 <= thr_hi16)
locked_after = in_range ? 1 : 0
  • Если locked_after==0 → тайл разлочивается, дети станут ACTIVE==0 в следующем тике.
  • Если locked_after==1 → drive_vec=row16_out (scaled VSB).

События:

  • LOCK_TRANSITION(t) = (locked_before==0 && locked_after==1)
  • FIRE(t) = LOCK_TRANSITION(t)
  • locked := locked_after

Инвариант v0.2:

  • locked==1 ⇒ thr_lo16 <= thr_cur16 <= thr_hi16

6.7 FUSE-LOCK (обязательный)

Если locked_after==1, тайл сохраняет состояние:

  • thr_cur сохраняется (с учётом decay),
  • тайл может писать на шину BUS16 если имеет флаг BUS_W,
  • Decay применяется (если decay16>0, thr_cur16 тянется к 0).

6.8 Drive selection (WRITE)

В конце READ для каждого тайла вычисляются два значения (раздел 6.5):

/* Для аккумулятора (delta): */
row_raw_signed[r] = Σ_{i=0..7} (in16[i] * Wmag[r][i] * sign)

/* Для шины (drive): */
weight_sum[r] = Σ_{i=0..7} (Wmag[r][i] * sign)
row16_out[r] = clamp15((max(in16[r] * weight_sum[r], 0) + 28) / 56)

В PHASE_WRITE:

  • если locked_after==1 и BUS_W==1: drive_vec[r] = row16_out[r] (тайл пишет scaled VSB на шину)
  • иначе: тайл не пишет на шину

Физический смысл: - Аккумулятор суммирует матричные произведения для обновления thr_cur - Шина масштабирует VSB ingress на сумму весов строки для эстафетной передачи


7) RoutingFlags16: карта битов

LSB-first:

  • bit0 N
  • bit1 E
  • bit2 S
  • bit3 W
  • bit4 NE
  • bit5 SE
  • bit6 SW
  • bit7 NW
  • bit8 BUS_R (источник ACTIVE)
  • bit9 BUS_W (писать в BUS в WRITE)
  • bit10..15 reserved=0

8) Граф активации (соседи) и координаты

8.1 Соседи по топологии

tile_id = y*tile_w + x

Кардинальные:

  • N(x,y)=(x,y-1) если y>0
  • S(x,y)=(x,y+1) если y<tile_h-1
  • W(x,y)=(x-1,y) если x>0
  • E(x,y)=(x+1,y) если x<tile_w-1

Диагонали:

  • NE(x,y)=(x+1,y-1) если x0
  • SE(x,y)=(x+1,y+1) если x<tile_w-1 и y<tile_h-1
  • SW(x,y)=(x-1,y+1) если x>0 и y<tile_h-1
  • NW(x,y)=(x-1,y-1) если x>0 и y>0

8.2 Рёбра активации

Если у тайла A установлен флаг Dir и сосед B=neighbor(A,Dir) существует → ребро A→B.

Это влияет только на вычисление ACTIVE (6.1). Данные не передаются.

Мульти-родители разрешены. Циклы разрешены (детерминизм через locked_before и least-fixed-point ACTIVE).


9) BUS Semantics: честное суммирование + CLIP/OVF

В PHASE_WRITE для каждой линии i=0..7:

contrib_from_all_tiles[i] =
  Σ_{t | (routing_flags16[t] & BUS_W)!=0 && (locked self || locked_ancestor)} drive_vec[t][i]

bus_raw[i]  = contrib_from_all_tiles[i]
BUS16[i]    = clamp15(bus_raw[i])
BUS_CLIP[i] = (bus_raw[i] > 15)
BUS_CLIP_ANY = OR_i BUS_CLIP[i]

OVF:

  • PHASE_WRITE (BUS_CLIP)
  • BUS_OVF_ANY = BUS_CLIP_ANY
  • OVF_ANY = BUS_OVF_ANY

Политика v0.2: только saturate (clamp15), без wrap/divide.


10) COLLIDE: домены и winner (как v0.1, но без DAG-зависимостей)

Определения в текущем tick:

  • FIRE(t) = (locked_before[t]==0 && locked_after[t]==1)
  • FIRED_SET(d) = { t | domain_id(t)=d && FIRE(t)=1 }
  • cnt(d)=|FIRED_SET(d)|

Правила:

  • cnt(d)=0 → нет winner, COLLIDE(d)=0
  • cnt(d)=1 → winner единственный, COLLIDE(d)=0
  • cnt(d)≥2 → COLLIDE(d)=1 и winner выбирается:

    1. max priority8
    2. при равенстве min tile_id

winner(d) = argmax_{t∈FIRED_SET(d)} (priority8(t), -tile_id(t))


11) AutoReset-by-Fire (межфазный доменный сброс) — v0.2

(опционально; если не нужно — можно выкинуть целиком)

Каждый тайл имеет baked reset_on_fire_mask16[t].

Маска авто-reset:

AUTO_RESET_MASK16 =
  OR_{d | cnt(d)>0} reset_on_fire_mask16[ winner(d) ]

Применение строго после READOUT_SAMPLE текущего EV_FLASH:

apply_reset_domain(AUTO_RESET_MASK16)

Эффект apply_reset_domain — как 6.3 для доменов в маске, кроме тайла‑сбрасывателя и всей цепочки его предков (они исключаются из reset).

Гарантия: winner всегда успевает locked/drive в текущем tick (reset только “между вспышками”).


12) READOUT_POLICY v0.2

Default: R0_RAW_BUS — readout = BUS16[0..7] после WRITE.

Опционально R1_DOMAIN_WINNER_ID32 возможно, но требует дисциплины “только winner драйвит ID”, иначе сумма разрушит ID. (Если нужно — добавим точный канон, сейчас не критично.)


13) Bake Binary TLV Spec v0.2 (несовместим с v0.1)

13.1 Общие правила

  • Little-endian.
  • TLV padding = 0, выравнивание value до 4-байт boundary.
  • CRC32 IEEE (zlib/crc32) по всем байтам blob от offset 0 до начала TLV_CRC32 (TLV_CRC32 header+value в CRC не входят).

13.2 Header (28 bytes)

BakeBlobHeader:

  • magic char[4] = "D8BK"
  • ver_major u16 = 2
  • ver_minor u16 = 0
  • flags u32
  • total_len u32
  • bake_id u32
  • profile_id u32
  • reserved0 u32 = 0

Header flags (u32):

bit 0: BAKE_FLAG_DOUBLE_STRAIT Если установлен, ядро выполняет ДВОЙНОЙ ПРОЛИВ: - Один и тот же входной аккорд обрабатывается ДВА такта - Решение выдаётся только после второго такта - Для Дирижёра это ОДИН EV_FLASH, но время выполнения удваивается - Используется для повышения селективности при малом расстоянии Хэмминга bits 1-31: reserved (должны быть 0)

13.3 TLV header (8 bytes)

  • type u16
  • tflags u16
  • len u32

  • value[len] + padding

13.4 TLV type map v0.2

  • TLV_TOPOLOGY (0x0100)
  • TLV_TILE_PARAMS_V2 (0x0121)
  • TLV_TILE_ROUTING_FLAGS16 (0x0131)
  • TLV_READOUT_POLICY (0x0140)
  • TLV_RESET_ON_FIRE_MASK16 (0x0150)
  • TLV_TILE_WEIGHTS_PACKED (0x0160)
  • TLV_TILE_FIELD_LIMIT (0x0170)
  • TLV_CRC32 (0xFFFE) — последний

13.5 Обязательные TLV

Все из списка выше обязательны, кроме того, что AutoReset можно сделать нулевыми масками.


14) TLV структуры (канон v0.2)

14.1 TLV_TOPOLOGY (0x0100), len=16

TopologyV0:

  • tile_count u32
  • tile_w u16
  • tile_h u16
  • lanes u8 (=8)
  • domains u8 (=16)
  • reserved u16 (=0)
  • reserved2 u32 (=0)

14.2 TLV_TILE_PARAMS_V2 (0x0121), len = tile_count * 13

TileParamsV2 (13 bytes per tile):

  • thr_lo16 i16 (bytes 0-1)
  • thr_hi16 i16 (bytes 2-3)
  • decay16 u16 (bytes 4-5)
  • domain_id4 u8 (low nibble, high nibble reserved=0) (byte 6)
  • priority8 u8 (byte 7)
  • pattern_id16 u16 (bytes 8-9)
  • flags8 u8 (=0 reserved) (byte 10)
  • reserved u16 (=0) (bytes 11-12)

14.3 TLV_TILE_ROUTING_FLAGS16 (0x0131), len = tile_count * 2

Per tile:

  • routing_flags16 u16 (LE)

Reserved bits 10..15 должны быть 0.

14.4 TLV_TILE_WEIGHTS_PACKED (0x0160), len = tile_count * 40

Как раньше:

  • 32 bytes: magnitudes Wmag[8][8] (64 × mag3∈[0..7], 4 бита каждый, старший бит=0)
  • 8 bytes: sign bits Wsign[8][8] (64 бита), LSB-first

14.5 TLV_RESET_ON_FIRE_MASK16 (0x0150), len = tile_count * 2

Per tile: reset_mask16 u16

14.6 TLV_READOUT_POLICY (0x0140), len=12

ReadoutPolicyV0:

  • mode u8 (0=R0_RAW_BUS, 1=R1_DOMAIN_WINNER_ID32)
  • reserved0 u8 (=0)
  • winner_domain_mask u16 (если mode=1)
  • settle_ns u16 (рекомендация для железа; эмуль может игнорировать)
  • reserved1 u16 (=0)
  • reserved2 u32 (=0)

14.7 TLV_CRC32 (0xFFFE), len=4

  • crc32 u32

14.8 TLV_TILE_FIELD_LIMIT (0x0170), len=4

  • tile_field_limit u32 (0 = full kTileCount)

15) Load-time validation (обязательное)

Загрузчик bake обязан:

  1. magic/ver/total_len
  2. наличие всех обязательных TLV
  3. строгие len:

    • TILE_PARAMS_V2: tile_count*13
    • TILE_ROUTING_FLAGS16: tile_count*2
    • TILE_WEIGHTS_PACKED: tile_count*40
    • RESET_ON_FIRE_MASK16: tile_count*2
  4. CRC32 по правилу “до TLV_CRC32”

  5. reserved-поля = 0; flags8==0; routing reserved bits 10..15 == 0 header.flags может содержать только BAKE_FLAG_DOUBLE_STRAIT
  6. thr_lo16 <= thr_hi16 для каждого тайла
  7. tile_count == tile_w * tile_h

16) Runtime FLAGS (минимум)

Island отдаёт FLAGS32 (минимум):

  • bit0 READY_LAST
  • bit1 OVF_ANY_LAST
  • bit2 COLLIDE_ANY_LAST

(маски OVF/COLLIDE опционально)


17) UDP Protocol (packet_v1, fixed binary)

Назначение: каскадирование машин. Пакеты одинаковые для IN и OUT.

Формат (37 bytes, little-endian):

Offset Size Field Description
0 4 magic u32 'D8UP' (0x50553844)
4 2 version u16 Версия протокола (=1)
6 2 flags u16 Биты: bit0=has_winner, bit1=has_bus, bit2=has_cycle, bit3=has_flags
8 4 frame_tag u32 Номер кадра (тег)
12 1 domain_id u8 ID домена-победителя (0..15, 0xFF если нет)
13 2 pattern_id u16 ID паттерна победителя
15 2 reset_mask16 u16 Маска доменов для сброса
17 2 collision_mask16 u16 Маска доменов с коллизией
19 2 winner_tile_id u16 ID тайла-победителя (0xFFFF если нет)
21 4 cycle_time_us u32 Время цикла в микросекундах
25 4 flags32_last u32 FLAGS32 с последнего flash
29 8 bus16[8] u8 Значения шины BUS16 (0..15 на линию)

Примечания:

  • reset_mask16 задаёт домены для RESET_DOMAIN перед flash.
  • collision_mask16/winner_tile_id/pattern_id валидны если flags & 0x0001 (has_winner).
  • bus16 валиден если flags & 0x0002 (has_bus).
  • cycle_time_us валиден если flags & 0x0004 (has_cycle).
  • flags32_last валиден если flags & 0x0008 (has_flags).

18) Cascade Configuration (каскад машин)

Назначение: объединение нескольких машин Decima-8 в каскад для обработки одного VSB потока.

Топология:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Machine   │────▶│   Machine   │────▶│   Machine   │
│     #1      │ UDP │     #2      │ UDP │     #3      │
│  (IDE/Host) │     │  (Solver)   │     │  (Solver)   │
└─────────────┘     └─────────────┘     └─────────────┘
      │                   │                   │
      │ UDP OUT           │ UDP OUT           │ UDP OUT
      │ port 9902         │ port 9902         │ port 9902
      ▼                   ▼                   ▼
      │ UDP IN            │ UDP IN            │ UDP IN
      │ port 9901         │ port 9901         │ port 9901

Конфигурация портов:

Машина UDP IN (приём) UDP OUT (отправка)
Machine #1 9901 9902
Machine #2 9902 9903
Machine #3 9903 9904

Порядок обработки:

  1. Machine #1 (IDE/Host):
  2. Генерирует VSB ingress (из TapeDeck или другого источника)
  3. Выполняет EV_FLASH локально
  4. Отправляет UDP пакет с решениями (winner_tile_id, bus16, reset_mask16)

  5. Machine #2..N (Solvers):

  6. Получают UDP пакет от предыдущей машины
  7. Выполняют RESET_DOMAIN по reset_mask16 из пакета
  8. Выполняют EV_FLASH с bus16 из пакета как VSB ingress
  9. Отправляют UDP пакет со своими решениями следующей машине

Важно:

  • reset_mask16 передаётся по каскаду для синхронизации сброса доменов.
  • bus16 на шине — это результат суммирования вкладов всех locked тайлов с BUS_W.
  • Каждая машина добавляет свои решения в SolutionsPanel независимо.

Пример команды для net_solver:

# Machine #2 (Solver 1)
net_solver -r 9902 -s 9903 -h 192.168.1.101 -R -v bake.d8p

# Machine #3 (Solver 2)
net_solver -r 9903 -s 9904 -h 192.168.1.102 -R -v bake.d8p

Параметры:

  • -r <port> — порт для приёма UDP (UDP IN)
  • -s <port> — порт для отправки UDP (UDP OUT)
  • -h <host> — IP адрес следующей машины в каскаде
  • -R — сброс доменов после каждого решения (рекомендуется)
  • -v — verbose режим (логирование решений)
  • -vv — полный trace (вход VSB + активация тайлов)

19) MUST для эмулятора (чтобы железо совпало)

  1. EV_FLASH всегда выполняет READ→WRITE, двойная буферизация (нельзя читать то, что пишешь).
  2. EV_RESET_DOMAIN и EV_BAKE только между EV_FLASH.
  3. ACTIVE closure — least fixed point по locked_before (6.1). Это обеспечивает "схлоп ветки" без лишних полуживых тиков.
  4. Если ACTIVE==0, тайл принудительно: thr_cur16=0, locked=0, без веса/decay/drive.
  5. Decay тянет к 0 и не перескакивает 0 (6.6).
  6. Lock по диапазону [thr_lo16..thr_hi16] (6.6).
  7. LOCK drive: drive_vec=row16_out при locked (6.8) — VSB масштабируется на сумму весов строки.

END OF CONTRACT v0.2