Signal-type popup, NC pin tagging, interactive viewer hygiene.

- Enter on a signal entry (net / explore) opens a modal popup to pick
  power / gnd / other. Recording is deduped: a sequence of toggles on
  the same signal collapses to a single `set-signal-type` line; no-op
  selections record nothing.
- Bare interactive commands (the ones that open a full-screen mode)
  are no longer recorded by `script-save`. Their inline forms still
  are. Mutating actions inside a screen record their own canonical
  line.
- Mentor importer treats signals whose name starts with `unconnected`
  as no-connect — the pin is kept on the part without a signal and
  tagged `ImportedUnconnected`.
- `drop_singleton_signals` runs at the end of `load`: any signal with
  exactly one pin is detached (singletons are NC by definition); the
  pin is tagged `DroppedSingleton`. Count is reported inline.
- `verify` gains a one-line orphan summary (imported NC / dropped
  singleton totals). Pins materialised by `FillIdentityNCs` are
  excluded via a `pin_map` filter — they are bridged to a real signal
  on the peer module and are not real NCs at system level.
- NcOrigin tag is serialized in save snapshots as an optional 4th
  field on N records (backward-compatible).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 07:56:46 +02:00
parent 043fef0a31
commit 280526304d
16 changed files with 295 additions and 16 deletions

View File

@@ -18,6 +18,7 @@
#include <cstdlib>
#include <exception>
#include <fstream>
#include <unordered_set>
#include <utility>
void Tui::RegisterCommands() {
@@ -132,9 +133,12 @@ void Tui::RegisterCommands() {
try {
sys->Load(args[0], args[1], t);
Module *mod = sys->modules()->get(args[0]);
int dropped = drop_singleton_signals(mod->signals);
Print("loaded '" + args[0] + "' from " + args[1]);
Print(" parts: " + std::to_string(mod->size()));
Print(" signals: " + std::to_string(mod->signals->size()));
Print(" signals: " + std::to_string(mod->signals->size())
+ (dropped ? " (dropped " + std::to_string(dropped)
+ " singleton/NC signal(s))" : ""));
} catch (const std::exception &e) {
Print(std::string("load failed: ") + e.what());
}
@@ -249,6 +253,30 @@ void Tui::RegisterCommands() {
Print("verify: " + std::to_string(inconsistent) + " inconsistent net(s) over "
+ std::to_string(bridged) + " bridged net(s) ("
+ std::to_string(nets.size()) + " total).");
// Orphan pin report. A pin is "orphan" if it came out of import (or
// post-import drop) with no signal, and is still not bridged to a
// real signal via any Connection::pin_map. Use `nc-export` for the
// per-pin list.
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);
}
int orph_imported = 0, orph_dropped = 0;
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;
if (pin->nc_origin == NcOrigin::ImportedUnconnected) ++orph_imported;
else if (pin->nc_origin == NcOrigin::DroppedSingleton) ++orph_dropped;
}
Print("verify: " + std::to_string(orph_imported + orph_dropped)
+ " orphan pin(s) at import ("
+ std::to_string(orph_imported) + " imported NC, "
+ std::to_string(orph_dropped) + " dropped singleton).");
}, true,
"check pin roles locally and signal-type consistency across bridged nets" };
@@ -653,6 +681,7 @@ void Tui::RegisterCommands() {
Pin *sn = nkv.second;
Pin *dn = new Pin(sn->name);
dn->expected_signal_type = sn->expected_signal_type;
dn->nc_origin = sn->nc_origin;
dp->add(dn);
if (sn->signal()) {
Signal *ds = dst->signals->get(sn->signal()->name);