Отладка по волнам
Вы научитесь
Как пройти от симптома к сигналу, такт за тактом, на настоящем измеренном баге.
Вот баг, найденный волнами, от начала до конца. Линк UART на стенде запросил 115,200 бод, а провод нёс 115,385 — ошибку +0.16 % от делителя частоты, невидимую в коде и очевидную в захвате. Запрошено 921,600 — провод несёт 923,077, та же дробная ошибка; 3,000,000 делится нацело и на проводе точно. Измерение называет исправление: брать делители, дающие целые частоты. Волны превращают симптом (потерянные байты) в число (0.16 %) и в правку.
Попробуйте
Посчитайте, какой делитель положил бы 115,200 на провод точно; сверьте с рядами 921,600 и 3,000,000 в виджете.

CP2102N on a direct USB port, no hub. Requested 115,200 puts 115,385 on the wire (+0.16 %), 921,600 puts 923,077 (+0.16 %), 3,000,000 is exact.
specs/fpga/testbench/vcd_trace_tb.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/fpga/testbench/vcd_trace_tb.t27
// VCD Trace Testbench
// Tests waveform dump generation, signal hierarchy, and timestamp management
// phi^2 + 1/phi^2 = 3 | TRINITY
module VCD_Trace_Testbench {
use fpga::vcd_trace::VcdTrace;
const CLK_PERIOD : u32 = 20;
const MAX_SIGNALS : u32 = 256;
const TIMESTAMP_RES_PS : u32 = 10;
var clk : bool = false;
var rst_n : bool = false;
var trace_en : bool = false;
var signal_count : u32 = 0;
var timestamp_ps : u32 = 0;
var dump_complete : bool = false;
var test_passed : u32 = 0;
var test_failed : u32 = 0;
fn tick() {
clk = false;
clk = true;
timestamp_ps = timestamp_ps + TIMESTAMP_RES_PS;
}
fn reset() {
rst_n = false;
tick();
tick();
rst_n = true;
tick();
}
fn advance_time(steps : u32) -> u32 {
var i : u32 = 0;
while i < steps {
tick();
i = i + 1;
}
return timestamp_ps;
}
fn format_timestamp(ps : u32) -> u32 {
return ps / 1000;
}
test test_reset_state {
reset();
invariant trace_en == false;
invariant dump_complete == false;
}
test test_timestamp_advance {
timestamp_ps = 0;
var t : u32 = advance_time(10);
invariant t == 100;
}
test test_format_timestamp {
var ns : u32 = format_timestamp(10000);
invariant ns == 10;
}
test test_max_signals {
invariant MAX_SIGNALS == 256;
}
test test_timestamp_resolution {
invariant TIMESTAMP_RES_PS == 10;
}
test test_tick {
tick();
invariant clk == true;
invariant timestamp_ps == 10;
}
invariant max_signals_positive : MAX_SIGNALS > 0;
invariant resolution_positive : TIMESTAMP_RES_PS > 0;
bench bench_vcd_trace {
reset();
trace_en = true;
advance_time(100);
trace_en = false;
}
}
Все уроки
Модуль 1 · Зачем проверять
Дизайны, которые компилируются и ошибаются; модель, которая выносит вердикт; план, записанный до кода.
Модуль 2 · Тестбенчи
Стимулы, проверки и вердикт, записанные как один spec рядом с дизайном, который они судят.
Модуль 3 · Временные диаграммы
Трасса каждого сигнала, прочитанная так, как её читает инженер по железу, и два прогона, сравнённые между собой.
Модуль 4 · Векторы соответствия
Случаи с ответом, записанным рядом, там, откуда их достанет компилятор.
Модуль 5 · Косимуляция
Spec, симулятор и плата сходятся в одном ответе на стенде Artix-7 XC7A200T, и что делать, когда не сходятся.
Модуль 6 · Покрытие
Чего коснулись тесты: строки, переключения, состояния — и что прячет это число.
Модуль 7 · Формальные методы
Ассерты, верные каждый такт; ограниченный поиск контрпримера; и почему доказательству нужна индукция.
Модуль 8 · Мутации
Ломайте дизайн нарочно и считайте, что заметили тесты.
Модуль 9 · Приёмка
Одна команда, все квитанции, чистый вердикт, который можно показать.