Signal analysis pass (analyze), NC tests, DESIGN.md catch-up.
- New `src/system/analysis.{hpp,cpp}` — stateless post-processing pass
`analyze_system(System*) → AnalysisReport`. Per-module detection of
signal groups and anomalies; pure read, re-runnable.
- Groups: diff pairs (`*_P` / `*_N`, case-insensitive), buses
(`NAME[N]` or strict `NAME_N` — the `_` before digits is required
so names like `GETH_01_VDD12` are not misread as a bus).
- Anomalies: `DiffPairOrphan` (asymmetric: only `_P` without `_N` is
reported — `_N` alone is overloaded with active-low semantics and
floods the output with false positives), `BusGap` (missing index
inside a detected `[lo..hi]`).
- Noise filters: signals starting with `$` (Mentor internals) are
skipped wholesale.
- New `analyze` shell command — prints groups sorted by module +
label, then anomalies. Sized for the upcoming dashboard.
- `tests/test_analysis.cpp` — 8 cases covering both detectors, false-
positive guards (no-underscore digits, `$`-prefixed internals), and
per-module scoping.
- `tests/test_nc_origin.cpp` — completes the prior NC-tagging commit
with round-trip + drop_singleton_signals coverage.
- DESIGN.md updated: layout entry for `analysis.{hpp,cpp}` and new
section explaining the pass; NC-origin paragraph aligned with the
actual tag semantics and the verify three-pass summary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "tui/tui.hpp"
|
||||
#include "tui/tui_helpers.hpp"
|
||||
|
||||
#include "system/analysis.hpp"
|
||||
#include "system/connect.hpp"
|
||||
#include "system/modules.hpp"
|
||||
#include "system/nets.hpp"
|
||||
@@ -280,6 +281,56 @@ void Tui::RegisterCommands() {
|
||||
}, true,
|
||||
"check pin roles locally and signal-type consistency across bridged nets" };
|
||||
|
||||
commands["analyze"] = { {}, [this](auto &) {
|
||||
if (!sys) { Print("no system: run 'new' first."); return; }
|
||||
AnalysisReport rep = analyze_system(sys.get());
|
||||
|
||||
int n_diff = 0, n_bus = 0;
|
||||
for (const auto &g : rep.groups) {
|
||||
if (g.kind == GroupKind::DiffPair) ++n_diff;
|
||||
else if (g.kind == GroupKind::Bus) ++n_bus;
|
||||
}
|
||||
int n_dp_orph = 0, n_bus_gap = 0;
|
||||
for (const auto &a : rep.anomalies) {
|
||||
if (a.kind == AnomalyKind::DiffPairOrphan) ++n_dp_orph;
|
||||
else if (a.kind == AnomalyKind::BusGap) ++n_bus_gap;
|
||||
}
|
||||
|
||||
Print("analyze: " + std::to_string(n_diff) + " diff pair(s), "
|
||||
+ std::to_string(n_bus) + " bus(es).");
|
||||
|
||||
// Sort groups by module then label so output is stable.
|
||||
auto by_label = [](const SignalGroup &a, const SignalGroup &b) {
|
||||
std::string ma = a.module ? a.module->name : std::string{};
|
||||
std::string mb = b.module ? b.module->name : std::string{};
|
||||
if (ma != mb) return ma < mb;
|
||||
return a.label < b.label;
|
||||
};
|
||||
auto groups = rep.groups; // copy: report stays untouched
|
||||
std::sort(groups.begin(), groups.end(), by_label);
|
||||
for (const auto &g : groups) {
|
||||
std::string mname = g.module ? g.module->name : std::string("?");
|
||||
std::string line = " " + mname + "/" + g.label
|
||||
+ " [" + group_kind_name(g.kind) + "]"
|
||||
+ " — " + std::to_string(g.members.size())
|
||||
+ " signal(s)";
|
||||
Print(line);
|
||||
}
|
||||
|
||||
if (rep.anomalies.empty()) {
|
||||
Print("analyze: no anomaly.");
|
||||
} else {
|
||||
Print("analyze: " + std::to_string(rep.anomalies.size())
|
||||
+ " anomaly(ies) ("
|
||||
+ std::to_string(n_dp_orph) + " diff-pair orphan, "
|
||||
+ std::to_string(n_bus_gap) + " bus gap):");
|
||||
for (const auto &a : rep.anomalies)
|
||||
Print(" [" + std::string(anomaly_kind_name(a.kind)) + "] "
|
||||
+ a.message);
|
||||
}
|
||||
}, true,
|
||||
"detect signal groups (diff pairs, buses) and structural anomalies" };
|
||||
|
||||
commands["net"] = {
|
||||
{{"module", Completion::None},
|
||||
{"signal name", Completion::None}},
|
||||
|
||||
Reference in New Issue
Block a user