Rename the power-adjacent category to "power management"

"Adjacent" read as jargon; "power management" is the standard EE umbrella
for enable/power-good/sense/fault/seq signals (cf. PMIC). Renamed across
the board: NameVerdict::PowerMgmt, stats/LoadResult field `mgmt`, analyze
tag [Power mgmt] + header "Pwr-mgmt" + glossary, load lines now say
"power-management (control/measure — kept as Other)" (TUI / script / wx
kept in sync).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 18:21:25 +02:00
parent e914c84c18
commit 9cf43696a2
12 changed files with 64 additions and 62 deletions

View File

@@ -36,7 +36,7 @@ LoadResult load_module(System *sys, const std::string &module_name,
r.power = inf.power; r.power = inf.power;
r.gnd = inf.gnd; r.gnd = inf.gnd;
r.kept_other = inf.kept_other; r.kept_other = inf.kept_other;
r.adjacent = inf.adjacent; r.mgmt = inf.mgmt;
r.ok = true; r.ok = true;
} catch (const std::exception &e) { } catch (const std::exception &e) {
r.error = e.what(); r.error = e.what();

View File

@@ -23,7 +23,7 @@ struct LoadResult {
int power = 0; ///< signals inferred Power (name + structure) int power = 0; ///< signals inferred Power (name + structure)
int gnd = 0; ///< signals inferred GndShield (name) int gnd = 0; ///< signals inferred GndShield (name)
int kept_other = 0; ///< name said Power but evidence too weak → kept Other int kept_other = 0; ///< name said Power but evidence too weak → kept Other
int adjacent = 0; ///< rail + control token (SENSE/EN/PG/…) → Other, not suspect int mgmt = 0; ///< power-management signal (rail + SENSE/EN/PG/… token) → Other, not suspect
}; };
// Import a module from a netlist/pinout file into `sys`, drop singleton signals, // Import a module from a netlist/pinout file into `sys`, drop singleton signals,

View File

@@ -190,8 +190,8 @@ private:
emit(" types: " + std::to_string(r.power) + " power, " emit(" types: " + std::to_string(r.power) + " power, "
+ std::to_string(r.gnd) + " gnd, " + std::to_string(r.kept_other) + std::to_string(r.gnd) + " gnd, " + std::to_string(r.kept_other)
+ " suspect Power (name only — kept as Other), " + " suspect Power (name only — kept as Other), "
+ std::to_string(r.adjacent) + std::to_string(r.mgmt)
+ " power-adjacent (control — kept as Other)"); + " power-management (control/measure — kept as Other)");
return true; return true;
} }
if (cmd == "connect" || cmd == "plug") { if (cmd == "connect" || cmd == "plug") {

View File

@@ -282,12 +282,12 @@ SignalTypeInferenceStats infer_signal_types(System *sys) {
++st.gnd; ++st.gnd;
continue; continue;
} }
if (ncl.verdict == NameVerdict::PowerAdjacent) { if (ncl.verdict == NameVerdict::PowerMgmt) {
// A rail token next to a control token (SENSE, EN, PG, …): // A rail token next to a control token (SENSE, EN, PG, …):
// a signal about a rail, confidently NOT the rail — never // a signal about a rail, confidently NOT the rail — never
// suspect, whatever the fan-out. // suspect, whatever the fan-out.
s->type = SignalType::Other; s->type = SignalType::Other;
++st.adjacent; ++st.mgmt;
continue; continue;
} }
if (ncl.verdict == NameVerdict::Rail) { if (ncl.verdict == NameVerdict::Rail) {

View File

@@ -62,8 +62,8 @@ struct SignalTypeInferenceStats {
int power = 0; ///< Signals promoted to Power (name + structural). int power = 0; ///< Signals promoted to Power (name + structural).
int gnd = 0; ///< Signals promoted to GndShield (name only). int gnd = 0; ///< Signals promoted to GndShield (name only).
int kept_other = 0; ///< Name said Power but structural evidence too weak. int kept_other = 0; ///< Name said Power but structural evidence too weak.
int adjacent = 0; ///< Rail token + control token (SENSE/EN/PG/…) → int mgmt = 0; ///< Power-management signal (rail + SENSE/EN/PG/…
///< confidently Other, never suspect. ///< token) → confidently Other, never suspect.
}; };
// Thresholds used by `infer_signal_types` (re-exposed so the analyze screen // Thresholds used by `infer_signal_types` (re-exposed so the analyze screen

View File

@@ -5,16 +5,17 @@
enum class SignalType { Power, GndShield, Other }; enum class SignalType { Power, GndShield, Other };
// Name-level verdict, richer than SignalType. `PowerAdjacent` is the key // Name-level verdict, richer than SignalType. `PowerMgmt` is the key
// addition: a name holding BOTH a rail token (VCC/VDD/PWR/…) and a control // addition: a name holding BOTH a rail token (VCC/VDD/PWR/…) and a control
// token (SENSE/EN/PG/FB/…) is a signal *about* a rail — measurement, enable, // token (SENSE/EN/PG/FB/…) is a power-management signal — measurement,
// power-good — not the rail itself. Its non-Power classification is therefore // enable, power-good — not the rail itself. Its non-Power classification is
// confident, where a bare rail name without structural evidence stays suspect. // therefore confident, where a bare rail name without structural evidence
enum class NameVerdict { Rail, GndShield, PowerAdjacent, Other }; // stays suspect.
enum class NameVerdict { Rail, GndShield, PowerMgmt, Other };
struct NameClassification { struct NameClassification {
NameVerdict verdict = NameVerdict::Other; NameVerdict verdict = NameVerdict::Other;
std::string token; ///< PowerAdjacent only: the control token that decided it. std::string token; ///< PowerMgmt only: the control token that decided it.
}; };
NameClassification classify_signal_name(const std::string &signal_name); NameClassification classify_signal_name(const std::string &signal_name);
@@ -22,7 +23,7 @@ NameClassification classify_signal_name(const std::string &signal_name);
const char *signal_type_name(SignalType t); const char *signal_type_name(SignalType t);
bool signal_type_from_name(const std::string &s, SignalType &out); bool signal_type_from_name(const std::string &s, SignalType &out);
// Thin wrapper over classify_signal_name: Rail → Power, GndShield → GndShield, // Thin wrapper over classify_signal_name: Rail → Power, GndShield → GndShield,
// PowerAdjacent/Other → Other. // PowerMgmt/Other → Other.
SignalType infer_signal_type(const std::string &signal_name); SignalType infer_signal_type(const std::string &signal_name);
SignalType next_signal_type(SignalType t); // Power → GndShield → Other → Power SignalType next_signal_type(SignalType t); // Power → GndShield → Other → Power

View File

@@ -101,7 +101,7 @@ std::vector<std::string> alnum_tokens(const std::string &u) {
// reliable there — left out of the control-token logic on purpose). Names // reliable there — left out of the control-token logic on purpose). Names
// containing PWR/POWER/VCC/VDD/VEE/VSS, or starting with VS_/VBAT/+/ → rail // containing PWR/POWER/VCC/VDD/VEE/VSS, or starting with VS_/VBAT/+/ → rail
// candidates; a rail candidate whose tokens include a control word (SENSE, // candidates; a rail candidate whose tokens include a control word (SENSE,
// EN, PG, …) is downgraded to PowerAdjacent. Else Other. // EN, PG, …) is downgraded to PowerMgmt. Else Other.
NameClassification classify_signal_name(const std::string &name) { NameClassification classify_signal_name(const std::string &name) {
NameClassification out; NameClassification out;
if (name.empty()) return out; if (name.empty()) return out;
@@ -133,7 +133,7 @@ NameClassification classify_signal_name(const std::string &name) {
|| starts_with("+") || starts_with("-")) { || starts_with("+") || starts_with("-")) {
for (const std::string &tok : alnum_tokens(u)) { for (const std::string &tok : alnum_tokens(u)) {
if (is_power_control_token(tok)) { if (is_power_control_token(tok)) {
out.verdict = NameVerdict::PowerAdjacent; out.verdict = NameVerdict::PowerMgmt;
out.token = tok; out.token = tok;
return out; return out;
} }
@@ -148,7 +148,7 @@ SignalType infer_signal_type(const std::string &name) {
switch (classify_signal_name(name).verdict) { switch (classify_signal_name(name).verdict) {
case NameVerdict::Rail: return SignalType::Power; case NameVerdict::Rail: return SignalType::Power;
case NameVerdict::GndShield: return SignalType::GndShield; case NameVerdict::GndShield: return SignalType::GndShield;
case NameVerdict::PowerAdjacent: case NameVerdict::PowerMgmt:
case NameVerdict::Other: break; case NameVerdict::Other: break;
} }
return SignalType::Other; return SignalType::Other;

View File

@@ -153,8 +153,8 @@ void Tui::RegisterCommands() {
+ std::to_string(r.gnd) + " gnd, " + std::to_string(r.gnd) + " gnd, "
+ std::to_string(r.kept_other) + std::to_string(r.kept_other)
+ " suspect Power (name only — kept as Other), " + " suspect Power (name only — kept as Other), "
+ std::to_string(r.adjacent) + std::to_string(r.mgmt)
+ " power-adjacent (control — kept as Other)"); + " power-management (control/measure — kept as Other)");
}, },
/*prompt_for_missing=*/ true, /*prompt_for_missing=*/ true,
"load a module from a netlist / pinout file (mentor, altium, ods)", "load a module from a netlist / pinout file (mentor, altium, ods)",

View File

@@ -131,7 +131,7 @@ Component Tui::BuildAnalyzeScreen() {
// ============================================================= Types // ============================================================= Types
// Power decisions (confirmed / refuted) and NC orphan breakdown. // Power decisions (confirmed / refuted) and NC orphan breakdown.
analyze_types.clear(); analyze_types.clear();
int conf_pwr = 0, ref_pwr = 0, adj = 0, gnd = 0; int conf_pwr = 0, ref_pwr = 0, mgmt = 0, gnd = 0;
struct Row { char kind; std::string mod, sig; int fanout; bool voltage; struct Row { char kind; std::string mod, sig; int fanout; bool voltage;
std::string token; }; std::string token; };
std::vector<Row> rows; std::vector<Row> rows;
@@ -148,8 +148,8 @@ Component Tui::BuildAnalyzeScreen() {
kind = 'P'; ++conf_pwr; kind = 'P'; ++conf_pwr;
} else if (ncl.verdict == NameVerdict::Rail && s->type == SignalType::Other) { } else if (ncl.verdict == NameVerdict::Rail && s->type == SignalType::Other) {
kind = 'R'; ++ref_pwr; kind = 'R'; ++ref_pwr;
} else if (ncl.verdict == NameVerdict::PowerAdjacent) { } else if (ncl.verdict == NameVerdict::PowerMgmt) {
kind = 'A'; ++adj; token = ncl.token; kind = 'M'; ++mgmt; token = ncl.token;
} else continue; } else continue;
rows.push_back({kind, mod->name, s->name, rows.push_back({kind, mod->name, s->name,
(int)s->size(), has_voltage_pattern(s->name), (int)s->size(), has_voltage_pattern(s->name),
@@ -157,9 +157,9 @@ Component Tui::BuildAnalyzeScreen() {
} }
} }
// Deliberate display order: confirmed rails, then the suspects (the // Deliberate display order: confirmed rails, then the suspects (the
// actionable residue), then the power-adjacent controls, gnd last. // actionable residue), then the power-management signals, gnd last.
auto rank = [](char k) { auto rank = [](char k) {
return k == 'P' ? 0 : k == 'R' ? 1 : k == 'A' ? 2 : 3; return k == 'P' ? 0 : k == 'R' ? 1 : k == 'M' ? 2 : 3;
}; };
std::sort(rows.begin(), rows.end(), std::sort(rows.begin(), rows.end(),
[&](const Row &a, const Row &b) { [&](const Row &a, const Row &b) {
@@ -191,8 +191,8 @@ Component Tui::BuildAnalyzeScreen() {
else reason = "name only — fan-out " else reason = "name only — fan-out "
+ std::to_string(r.fanout) + std::to_string(r.fanout)
+ ", no voltage"; + ", no voltage";
} else if (r.kind == 'A') { } else if (r.kind == 'M') {
tag = "[Pwr-adjacent] "; tag = "[Power mgmt] ";
reason = "control token '" + r.token reason = "control token '" + r.token
+ "' in name — kept as Other"; + "' in name — kept as Other";
} else { } else {
@@ -214,8 +214,8 @@ Component Tui::BuildAnalyzeScreen() {
std::string types_header = "Types: " + std::to_string(conf_pwr) std::string types_header = "Types: " + std::to_string(conf_pwr)
+ " Power, " + std::to_string(ref_pwr) + " Power, " + std::to_string(ref_pwr)
+ " Suspect, " + std::to_string(adj) + " Suspect, " + std::to_string(mgmt)
+ " Adjacent, " + std::to_string(gnd) + " Pwr-mgmt, " + std::to_string(gnd)
+ " Gnd"; + " Gnd";
// Tab bar — horizontal headers, active one inverted. // Tab bar — horizontal headers, active one inverted.
@@ -258,10 +258,11 @@ Component Tui::BuildAnalyzeScreen() {
"Name suggests Power, no control token explains it, but the " "Name suggests Power, no control token explains it, but the "
"structural check failed — fan-out too low and no voltage " "structural check failed — fan-out too low and no voltage "
"in the name."), "in the name."),
term("Pwr-adjacent", term("Power mgmt",
"Name holds a rail token AND a control token (SENSE, EN, PG, " "Name holds a rail token AND a control token (SENSE, EN, PG, "
"FB, …): a signal about a rail — measurement or control " "FB, …): a power-management signal — measurement or control "
"not the rail itself. Confidently Other, never suspect."), "of a rail — not the rail itself. Confidently Other, never "
"suspect."),
term("Hard floor", term("Hard floor",
"Fan-out below 3 pins forces Other regardless of the name. " "Fan-out below 3 pins forces Other regardless of the name. "
"A real rail physically cannot live on 1-2 pads."), "A real rail physically cannot live on 1-2 pads."),

View File

@@ -352,9 +352,9 @@ void EssimFrame::OnLoad(wxCommandEvent &) {
} }
Log(wxString::Format( Log(wxString::Format(
"loaded '%s' from %s — %d part(s), %d signal(s)" "loaded '%s' from %s — %d part(s), %d signal(s)"
" (dropped %d; types: %d power / %d gnd / %d suspect / %d pwr-adjacent)", " (dropped %d; types: %d power / %d gnd / %d suspect / %d pwr-mgmt)",
modname, path, r.parts, r.signals, r.dropped, r.power, r.gnd, modname, path, r.parts, r.signals, r.dropped, r.power, r.gnd,
r.kept_other, r.adjacent)); r.kept_other, r.mgmt));
RebuildModelView(); RebuildModelView();
} }

View File

@@ -235,7 +235,7 @@ TEST_CASE("infer_signal_types: Power requires name+structural agreement") {
Signal *p_3v3 = m->signals->merge("PWR_3V3"); fan_out(p_3v3, 3); // voltage + ≥ floor → Power Signal *p_3v3 = m->signals->merge("PWR_3V3"); fan_out(p_3v3, 3); // voltage + ≥ floor → Power
Signal *vcc = m->signals->merge("VCC"); fan_out(vcc, 5); // fan-out ≥ 4 → Power Signal *vcc = m->signals->merge("VCC"); fan_out(vcc, 5); // fan-out ≥ 4 → Power
Signal *pwr_ok = m->signals->merge("PWR_OK"); fan_out(pwr_ok, 1); // control token → adjacent Signal *pwr_ok = m->signals->merge("PWR_OK"); fan_out(pwr_ok, 1); // control token → pwr-mgmt
Signal *pwr_2 = m->signals->merge("PWR_2"); fan_out(pwr_2, 2); // < 3 → hard floor → Other Signal *pwr_2 = m->signals->merge("PWR_2"); fan_out(pwr_2, 2); // < 3 → hard floor → Other
Signal *gnd = m->signals->merge("GND"); fan_out(gnd, 1); // gnd: name alone Signal *gnd = m->signals->merge("GND"); fan_out(gnd, 1); // gnd: name alone
Signal *clk = m->signals->merge("CLK_50MHZ"); fan_out(clk, 3); // not power-ish → Other Signal *clk = m->signals->merge("CLK_50MHZ"); fan_out(clk, 3); // not power-ish → Other
@@ -244,7 +244,7 @@ TEST_CASE("infer_signal_types: Power requires name+structural agreement") {
CHECK(st.power == 2); // PWR_3V3, VCC CHECK(st.power == 2); // PWR_3V3, VCC
CHECK(st.gnd == 1); // GND (name alone) CHECK(st.gnd == 1); // GND (name alone)
CHECK(st.kept_other == 1); // PWR_2 below the hard floor CHECK(st.kept_other == 1); // PWR_2 below the hard floor
CHECK(st.adjacent == 1); // PWR_OK: power-good control, not suspect CHECK(st.mgmt == 1); // PWR_OK: power-good control, not suspect
CHECK(p_3v3->type == SignalType::Power); CHECK(p_3v3->type == SignalType::Power);
CHECK(vcc->type == SignalType::Power); CHECK(vcc->type == SignalType::Power);
@@ -254,14 +254,14 @@ TEST_CASE("infer_signal_types: Power requires name+structural agreement") {
CHECK(clk->type == SignalType::Other); CHECK(clk->type == SignalType::Other);
} }
TEST_CASE("infer_signal_types: power-adjacent beats fan-out — a big sense net is still Other") { TEST_CASE("infer_signal_types: power-management beats fan-out — a big sense net is still Other") {
auto sys = std::make_unique<System>(); auto sys = std::make_unique<System>();
Module *m = sys->modules()->merge("M"); Module *m = sys->modules()->merge("M");
Part *p = new Part("U1"); m->add(p); Part *p = new Part("U1"); m->add(p);
// VDD_CORE_SENSE with fan-out 5: structure alone would confirm Power, // VDD_CORE_SENSE with fan-out 5: structure alone would confirm Power,
// but the control token settles it as a measurement net → Other, // but the control token settles it as a measurement net → Other,
// counted adjacent (not suspect, not power). // counted mgmt (not suspect, not power).
Signal *s = m->signals->merge("VDD_CORE_SENSE"); Signal *s = m->signals->merge("VDD_CORE_SENSE");
for (int i = 0; i < 5; ++i) { for (int i = 0; i < 5; ++i) {
Pin *pin = new Pin("p" + std::to_string(i)); Pin *pin = new Pin("p" + std::to_string(i));
@@ -271,7 +271,7 @@ TEST_CASE("infer_signal_types: power-adjacent beats fan-out — a big sense net
auto st = infer_signal_types(sys.get()); auto st = infer_signal_types(sys.get());
CHECK(st.power == 0); CHECK(st.power == 0);
CHECK(st.kept_other == 0); CHECK(st.kept_other == 0);
CHECK(st.adjacent == 1); CHECK(st.mgmt == 1);
CHECK(s->type == SignalType::Other); CHECK(s->type == SignalType::Other);
} }

View File

@@ -51,40 +51,40 @@ TEST_CASE("infer_signal_type: power family") {
CHECK(infer_signal_type("VS3_5V0") == SignalType::Power); CHECK(infer_signal_type("VS3_5V0") == SignalType::Power);
CHECK(infer_signal_type("+5V") == SignalType::Power); CHECK(infer_signal_type("+5V") == SignalType::Power);
CHECK(infer_signal_type("-12V") == SignalType::Power); CHECK(infer_signal_type("-12V") == SignalType::Power);
// Rail token + control token → power-adjacent, mapped to Other by the // Rail token + control token → power-management, mapped to Other by the
// wrapper (it is a sense line ABOUT VBAT, not the rail). // wrapper (it is a sense line ABOUT VBAT, not the rail).
CHECK(infer_signal_type("VBAT_SENSE") == SignalType::Other); CHECK(infer_signal_type("VBAT_SENSE") == SignalType::Other);
} }
TEST_CASE("classify_signal_name: rail vs power-adjacent control signals") { TEST_CASE("classify_signal_name: rail vs power-management signals") {
CHECK(classify_signal_name("VCC").verdict == NameVerdict::Rail); CHECK(classify_signal_name("VCC").verdict == NameVerdict::Rail);
CHECK(classify_signal_name("VDD_3V3").verdict == NameVerdict::Rail); CHECK(classify_signal_name("VDD_3V3").verdict == NameVerdict::Rail);
CHECK(classify_signal_name("+5V").verdict == NameVerdict::Rail); CHECK(classify_signal_name("+5V").verdict == NameVerdict::Rail);
NameClassification c = classify_signal_name("VDD_CORE_SENSE"); NameClassification c = classify_signal_name("VDD_CORE_SENSE");
CHECK(c.verdict == NameVerdict::PowerAdjacent); CHECK(c.verdict == NameVerdict::PowerMgmt);
CHECK(c.token == "SENSE"); CHECK(c.token == "SENSE");
CHECK(classify_signal_name("VBAT_SENSE").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VBAT_SENSE").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VCC_EN").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VCC_EN").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VCC_EN1").verdict == NameVerdict::PowerAdjacent); // trailing digit CHECK(classify_signal_name("VCC_EN1").verdict == NameVerdict::PowerMgmt); // trailing digit
CHECK(classify_signal_name("VDD_FB").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VDD_FB").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("PWR_GOOD").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("PWR_GOOD").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("PWR_OK").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("PWR_OK").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VBUS_DET").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VBUS_DET").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("POWER_FAIL").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("POWER_FAIL").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VDD_VSENSE").verdict == NameVerdict::PowerAdjacent); // fused suffix CHECK(classify_signal_name("VDD_VSENSE").verdict == NameVerdict::PowerMgmt); // fused suffix
CHECK(classify_signal_name("PWR_NFAULT").verdict == NameVerdict::PowerAdjacent); // active-low CHECK(classify_signal_name("PWR_NFAULT").verdict == NameVerdict::PowerMgmt); // active-low
CHECK(classify_signal_name("VDD_ADJ").verdict == NameVerdict::PowerAdjacent); // regulator adjust CHECK(classify_signal_name("VDD_ADJ").verdict == NameVerdict::PowerMgmt); // regulator adjust
CHECK(classify_signal_name("VCC_TRIM").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VCC_TRIM").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VDD_VTRIM").verdict == NameVerdict::PowerAdjacent); // fused suffix CHECK(classify_signal_name("VDD_VTRIM").verdict == NameVerdict::PowerMgmt); // fused suffix
CHECK(classify_signal_name("VCC_VSET").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VCC_VSET").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("VCC_FBK").verdict == NameVerdict::PowerAdjacent); // feedback variants CHECK(classify_signal_name("VCC_FBK").verdict == NameVerdict::PowerMgmt); // feedback variants
CHECK(classify_signal_name("VDD_FDB").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VDD_FDB").verdict == NameVerdict::PowerMgmt);
CHECK(classify_signal_name("PWR_CMD").verdict == NameVerdict::PowerAdjacent); // command CHECK(classify_signal_name("PWR_CMD").verdict == NameVerdict::PowerMgmt); // command
CHECK(classify_signal_name("PWR_LED").verdict == NameVerdict::PowerAdjacent); // indicator CHECK(classify_signal_name("PWR_LED").verdict == NameVerdict::PowerMgmt); // indicator
CHECK(classify_signal_name("VDD_REF").verdict == NameVerdict::PowerAdjacent); // reference CHECK(classify_signal_name("VDD_REF").verdict == NameVerdict::PowerMgmt); // reference
CHECK(classify_signal_name("VCC_VREF").verdict == NameVerdict::PowerAdjacent); CHECK(classify_signal_name("VCC_VREF").verdict == NameVerdict::PowerMgmt);
// Whole-token matching: SENSOR is not SENSE, GREEN is not EN — // Whole-token matching: SENSOR is not SENSE, GREEN is not EN —
// these stay genuine rails. // these stay genuine rails.