t27.aiEnglish

Складывать на каждом такте

Вы узнаете

Что on_clock добавляет к скалярному произведению и почему тесты этой спеки не доходят до регистра.

stream_ternary_mac.t27 берёт тот же dot27 и добавляет регистр acc и on_clock, который прибавляет dot27(a, b) к acc. on_clock тактируемый: он срабатывает раз на фронт такта, а acc хранит значение между фронтами. В сгенерированном Verilog низкий rst_n обнуляет acc, а acc меняется, только пока en высокий; в самой спеке en нет. Нативный t27c запускает все 4 теста, все проходят, ни один не пустой; браузер их пропускает: он не знает assert_eq. Все 4 вызывают dot27, ни один не запускает on_clock, так что строку накопления проверяет виджет test-waves, а не тест. Запись сужает маску для a в строке 26 с 3 до 1, и P в a читается как N: падает ровно один тест, dot_all_p_x_all_p. Каждый байт в записи напечатала команда; инсценирован только набор текста.

Попробуйте

Найдите в записи изменённую маску и упавший тест; затем найдите в спеке on_clock и убедитесь, что ни один тест его не вызывает.

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

t27c on stream_ternary_mac.t27 -- Accumulate on the clock, native
t27c on stream_ternary_mac.t27 -- Accumulate on the clock, native ↗

t27c on the t27c lab (Railway), spec at t27 5ff0ec512: 4 tests pass natively; a lane mask that loses a bit fails exactly one test, dot_all_p_x_all_p; git restores the spec.

specs/ternary/stream_ternary_mac.t27

module StreamTernaryMac;
// #1764: a STREAMING ternary MAC -- the on-hardware BitNet inference primitive.
//
// Each clock cycle consumes one packed 27-trit (a, b) pair on input data ports
// and accumulates their dot product into a registered accumulator. This is a
// real datapath generated entirely from spec:
//     input ports (a, b) -> ternary sign-multiply adder-tree (dot27)
//         -> accumulate register (acc) -> output port (acc)
// It synthesizes to Artix-7 fabric (yosys synth_xilinx: the dot27 adder-tree in
// LUTs + a 32-bit accumulate register in FDCE + CARRY4), and `en` gates the
// accumulation so a caller can stream N vectors then read the running sum.
//
// The dot27 / tmul / tp primitives are the bit-exact-verified ones from
// ternary_mac.t27 (#1743, cross-checked vs an independent reference on 300
// random vectors). The only new piece is the `on_clock` streaming wrapper.

// Sign-only ternary multiply of two packed trits {N=0b00, Z=0b01, P=0b10}.
fn tmul(ta: u8, tb: u8) -> i8 {
    if (ta == 1) { return 0; }
    if (tb == 1) { return 0; }
    if (ta == tb) { return 1; }
    return -1;
}
// One trit position i of two 54-bit packed vectors (trit i at [2i+1:2i]).
fn tp(a: u64, b: u64, i: u32) -> i8 {
    return tmul(((a >> (i << 1)) & 3) as u8, ((b >> (i << 1)) & 3) as u8);
}
// 27-trit ternary dot product, loop-free. Result in [-27, +27].
fn dot27(a: u64, b: u64) -> i8 {
    return tp(a,b,0) + tp(a,b,1) + tp(a,b,2) + tp(a,b,3) + tp(a,b,4)
         + tp(a,b,5) + tp(a,b,6) + tp(a,b,7) + tp(a,b,8) + tp(a,b,9)
         + tp(a,b,10) + tp(a,b,11) + tp(a,b,12) + tp(a,b,13) + tp(a,b,14)
         + tp(a,b,15) + tp(a,b,16) + tp(a,b,17) + tp(a,b,18) + tp(a,b,19)
         + tp(a,b,20) + tp(a,b,21) + tp(a,b,22) + tp(a,b,23) + tp(a,b,24)
         + tp(a,b,25) + tp(a,b,26);
}

// Registered accumulator, exposed as an output data port. i32 so it never
// overflows across a realistic stream (each step adds a value in [-27, +27]).
var acc : i32 = 0

// The clocked process: `a` and `b` are streaming input data ports; each cycle
// (while `en`) the dot product of the current pair is added to `acc`.
fn on_clock(a: u64, b: u64) {
    acc = acc + (dot27(a, b) as i32)
}

test dot_all_n_x_all_n { assert_eq(dot27(0, 0), 27); }
test dot_all_n_x_all_p { assert_eq(dot27(0, 12009599006321322), -27); }
test dot_all_p_x_all_p { assert_eq(dot27(12009599006321322, 12009599006321322), 27); }
test dot_all_z { assert_eq(dot27(6004799503160661, 6004799503160661), 0); }
endmodule

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

Все уроки