The analyze Issues pane and the dashboard Health rows each recomputed the same verify passes inline (pin-role mismatches, Power/Gnd net-mix, NC orphan rollup, model-driven checks) — the third and second copies of what the verify command also did. Route both screens through app::verify(System*) instead, so the passes live in exactly one place. Enrich VerifyReport with a per-pin OrphanPin detail list (module/part/pin + dropped flag) so the dashboard can still nest its dropped-singleton breakdown under the NC health line without re-walking modules/parts/pins. Output is unchanged in both screens (same label formats, same numbers). Prune the now-dead includes (nets/bsdl_check/connect/parts/pins as applicable, <unordered_set>) from both screens. Extend tests/test_verify.cpp to cover the new orphans detail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
3.6 KiB
C++
106 lines
3.6 KiB
C++
#include "core/app/verify.hpp"
|
|
|
|
#include "core/domain/bsdl_check.hpp"
|
|
#include "core/domain/connect.hpp"
|
|
#include "core/domain/modules.hpp"
|
|
#include "core/domain/nets.hpp"
|
|
#include "core/domain/parts.hpp"
|
|
#include "core/domain/pins.hpp"
|
|
#include "core/domain/signals.hpp"
|
|
#include "core/domain/system.hpp"
|
|
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace app {
|
|
|
|
VerifyReport verify(System *sys)
|
|
{
|
|
VerifyReport r;
|
|
if (!sys)
|
|
return r;
|
|
|
|
// Pass 1 — typed pins: expected (model) vs actual (net) signal type.
|
|
for (auto &mkv : *sys->modules()) {
|
|
Module *mod = mkv.second;
|
|
for (auto &pkv : *mod) {
|
|
Part *prt = pkv.second;
|
|
if (prt->connector_type.empty())
|
|
continue;
|
|
for (auto &nkv : *prt) {
|
|
Pin *pin = nkv.second;
|
|
++r.typed_pins;
|
|
SignalType expected = pin->expected_signal_type();
|
|
if (expected == SignalType::Other)
|
|
continue;
|
|
Signal *s = pin->signal();
|
|
SignalType actual = s ? s->type : SignalType::Other;
|
|
if (actual == expected)
|
|
continue;
|
|
RoleMismatch m;
|
|
m.module = mod->name;
|
|
m.part = prt->name;
|
|
m.pin = pin->name;
|
|
m.signal = s ? s->name : std::string("(NC)");
|
|
m.expected = expected;
|
|
m.actual = actual;
|
|
r.role_mismatches.push_back(std::move(m));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pass 2 — bridged nets: flag Power/GndShield mixing. Compute the nets once
|
|
// here and reuse them for the model checks below.
|
|
std::vector<Net> nets = compute_all_nets(sys);
|
|
r.total_nets = (int)nets.size();
|
|
for (const Net &n : nets) {
|
|
if (n.members.size() < 2)
|
|
continue;
|
|
++r.bridged_nets;
|
|
SignalType dom;
|
|
if (net_type_consistent(n, dom))
|
|
continue;
|
|
NetInconsistency ni;
|
|
for (const auto &mp : n.members)
|
|
ni.members.push_back({mp.first->name, mp.second->name, mp.second->type});
|
|
r.net_inconsistencies.push_back(std::move(ni));
|
|
}
|
|
|
|
// Pass 3 — orphans: pins with no signal and not bridged via a connection.
|
|
std::unordered_set<Pin *> bridged_pins;
|
|
for (auto &ckv : *sys->connections())
|
|
for (auto &wp : ckv.second->pin_map) {
|
|
if (wp.first) bridged_pins.insert(wp.first);
|
|
if (wp.second) bridged_pins.insert(wp.second);
|
|
}
|
|
for (auto &mkv : *sys->modules())
|
|
for (auto &pkv : *mkv.second)
|
|
for (auto &nkv : *pkv.second) {
|
|
Pin *pin = nkv.second;
|
|
if (pin->signal() || bridged_pins.count(pin))
|
|
continue;
|
|
bool dropped;
|
|
if (pin->nc_origin == NcOrigin::ImportedUnconnected) {
|
|
++r.orphan_imported;
|
|
dropped = false;
|
|
} else if (pin->nc_origin == NcOrigin::DroppedSingleton) {
|
|
++r.orphan_dropped;
|
|
dropped = true;
|
|
} else {
|
|
continue;
|
|
}
|
|
r.orphans.push_back({mkv.first, pkv.first, nkv.first, dropped});
|
|
}
|
|
|
|
// Passes 4-7 — model-driven checks (reuse the nets from pass 2).
|
|
r.pin_anomalies = check_pin_specs(sys, &nets);
|
|
r.jtag_anomalies = check_jtag_chain(sys, &nets);
|
|
r.conflict_anomalies = check_source_conflicts(sys);
|
|
r.completeness_anomalies = check_bsdl_completeness(sys);
|
|
|
|
return r;
|
|
}
|
|
|
|
} // namespace app
|