P3.3: surface model anomalies in analyze + dashboard

The analyze screen's Issues pane now lists the model-driven checks
(check_pin_specs / check_jtag_chain / check_source_conflicts) alongside the
pin-role, net-mix and structural ones, with an "N model" count in the header;
the dashboard gains a "model:" health row. check_pin_specs/check_jtag_chain
take an optional precomputed net list, so verify, analyze and the dashboard
each compute the nets once and reuse them across checks instead of redoing the
transitive closure per check. Unit tests (75) green; verify output unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 16:14:41 +02:00
parent fe5b2c3d96
commit c9ac186a20
6 changed files with 51 additions and 13 deletions

View File

@@ -39,13 +39,16 @@ std::string join_labels(const std::vector<Pin *> &pins)
} // namespace
std::vector<Anomaly> check_pin_specs(System *sys)
std::vector<Anomaly> check_pin_specs(System *sys, const std::vector<Net> *nets)
{
std::vector<Anomaly> out;
if (!sys)
return out;
for (const Net &net : compute_all_nets(sys)) {
std::vector<Net> local;
if (!nets)
local = compute_all_nets(sys);
for (const Net &net : (nets ? *nets : local)) {
std::vector<Pin *> pins;
std::vector<Signal *> sigs;
Module *mod = nullptr;
@@ -123,14 +126,17 @@ std::vector<Anomaly> check_pin_specs(System *sys)
return out;
}
std::vector<Anomaly> check_jtag_chain(System *sys)
std::vector<Anomaly> check_jtag_chain(System *sys, const std::vector<Net> *nets_in)
{
std::vector<Anomaly> out;
if (!sys)
return out;
// Map every pin to the index of the net it sits on.
std::vector<Net> nets = compute_all_nets(sys);
std::vector<Net> local;
if (!nets_in)
local = compute_all_nets(sys);
const std::vector<Net> &nets = nets_in ? *nets_in : local;
std::unordered_map<Pin *, int> net_of;
for (size_t i = 0; i < nets.size(); ++i)
for (auto &mp : nets[i].members)

View File

@@ -2,11 +2,16 @@
#define _BSDL_CHECK_HPP_
#include "analysis.hpp" // Anomaly, AnomalyKind
#include "nets.hpp" // Net
#include <vector>
class System;
// The net checks below accept an optional precomputed net list: callers that
// already have one (verify, the analyze screen, the dashboard) pass it so the
// transitive-closure pass isn't redone. Pass nullptr to compute it internally.
// Model-driven pin checks over the system's nets, using the PinSpec
// direction/function populated by connector or BSDL models. Emits:
// - DriveContention : a net with ≥2 push-pull output drivers;
@@ -14,7 +19,7 @@ class System;
// - NcWired : a no-connect pin wired onto a multi-pin net.
// Read-only; nets with no direction data are skipped (no false positives on
// un-modelled parts).
std::vector<Anomaly> check_pin_specs(System *sys);
std::vector<Anomaly> check_pin_specs(System *sys, const std::vector<Net> *nets = nullptr);
// JTAG boundary-scan chain integrity, using pins whose PinSpec.function is a TAP
// role (JtagTdi/Tdo/Tms/Tck/Trst). Resolves each TAP pin to its net and checks:
@@ -23,7 +28,7 @@ std::vector<Anomaly> check_pin_specs(System *sys);
// - JtagChainBreak : the TDO→TDI daisy chain dangles, fans out, or is not a
// single path (≠1 head / ≠1 tail).
// Empty when the system has no TAP pins.
std::vector<Anomaly> check_jtag_chain(System *sys);
std::vector<Anomaly> check_jtag_chain(System *sys, const std::vector<Net> *nets = nullptr);
// Conflicts between a device model and the netlist's own view of a pin. Today:
// a pin the BSDL declares power/ground (a must-connect rail) that the netlist

View File

@@ -300,14 +300,14 @@ void Tui::RegisterCommands() {
// Model-driven pin checks (drive contention / undriven net / NC-wired)
// from the PinSpec direction/function populated by connector/BSDL models.
auto pin_anoms = check_pin_specs(sys.get());
auto pin_anoms = check_pin_specs(sys.get(), &nets);
for (const auto &a : pin_anoms)
Print(" [" + std::string(anomaly_kind_name(a.kind)) + "] " + a.message);
Print("verify: " + std::to_string(pin_anoms.size())
+ " model-driven pin anomaly(ies).");
// JTAG boundary-scan chain integrity (TAP pins → nets).
auto jtag_anoms = check_jtag_chain(sys.get());
auto jtag_anoms = check_jtag_chain(sys.get(), &nets);
for (const auto &a : jtag_anoms)
Print(" [" + std::string(anomaly_kind_name(a.kind)) + "] " + a.message);
Print("verify: " + std::to_string(jtag_anoms.size())

View File

@@ -2,6 +2,7 @@
#include "tui/tui_helpers.hpp"
#include "system/analysis.hpp"
#include "system/bsdl_check.hpp"
#include "system/connect.hpp"
#include "system/modules.hpp"
#include "system/nets.hpp"
@@ -99,16 +100,33 @@ Component Tui::BuildAnalyzeScreen() {
+ anomaly_kind_name(a.kind) + "] "
+ a.message);
// Model-driven checks (same as `verify`), reusing the nets above.
std::vector<Anomaly> model_anoms;
{
auto a1 = check_pin_specs(sys.get(), &nets);
auto a2 = check_jtag_chain(sys.get(), &nets);
auto a3 = check_source_conflicts(sys.get());
model_anoms.insert(model_anoms.end(), a1.begin(), a1.end());
model_anoms.insert(model_anoms.end(), a2.begin(), a2.end());
model_anoms.insert(model_anoms.end(), a3.begin(), a3.end());
}
for (const auto &a : model_anoms)
analyze_issues.push_back(std::string("[")
+ anomaly_kind_name(a.kind) + "] "
+ a.message);
int n_model = (int)model_anoms.size();
if (analyze_issues.empty()) analyze_issues.push_back("(no issue found)");
if (analyze_issue_idx >= (int)analyze_issues.size())
analyze_issue_idx = (int)analyze_issues.size() - 1;
std::string issues_header = "Issues ("
+ std::to_string(n_role_mismatches + n_inconsistent
+ (int)rep.anomalies.size())
+ (int)rep.anomalies.size() + n_model)
+ ": " + std::to_string(n_role_mismatches) + " pin-role, "
+ std::to_string(n_inconsistent) + " net-mix, "
+ std::to_string(rep.anomalies.size()) + " struct.)";
+ std::to_string(rep.anomalies.size()) + " struct, "
+ std::to_string(n_model) + " model)";
// ============================================================ Groups
analyze_groups.clear();

View File

@@ -2,6 +2,7 @@
#include "tui/tui_helpers.hpp"
#include "system/analysis.hpp"
#include "system/bsdl_check.hpp"
#include "system/connect.hpp"
#include "system/modules.hpp"
#include "system/nets.hpp"
@@ -149,6 +150,14 @@ Component Tui::BuildDashboardScreen() {
+ std::to_string(orph_imported) + " imported, "
+ std::to_string(orph_dropped) + " dropped)"));
// Model-driven checks (BSDL pin specs, JTAG chain, source conflicts),
// reusing the nets computed above.
int n_model = (int)(check_pin_specs(sys.get(), &nets).size()
+ check_jtag_chain(sys.get(), &nets).size()
+ check_source_conflicts(sys.get()).size());
health_rows.push_back(health_line(n_model == 0,
"model: " + std::to_string(n_model) + " BSDL/JTAG anomaly(ies)"));
// ---- analysis summary ----
AnalysisReport rep = analyze_system(sys.get());
int n_diff = 0, n_diff_bus = 0, n_bus = 0;