#include #include "core/app/verify.hpp" #include "core/domain/analysis.hpp" #include "core/domain/connect.hpp" #include "core/domain/modules.hpp" #include "core/domain/parts.hpp" #include "core/domain/pins.hpp" #include "core/domain/signals.hpp" #include "core/domain/system.hpp" // check_diff_crossings: a complete local diff pair (X_P / X_N) must cross a // connection leg for leg. Both sides are judged by name only. namespace { // New pin on `part`, wired to (or creating) module signal `sig_name`. Pin *wire(Module *m, Part *p, const std::string &pin_name, const std::string &sig_name) { Pin *pin = new Pin(pin_name); p->add(pin); Signal *s = m->signals->merge(sig_name); s->add(pin); pin->connect(s); return pin; } struct Rig { System sys; Module *a, *b; Part *ja, *jb; Rig() { a = sys.modules()->merge("A"); b = sys.modules()->merge("B"); ja = new Part("J1"); a->add(ja); jb = new Part("P1"); b->add(jb); } void bridge(std::initializer_list> wires) { Connection *c = new Connection("A/J1 <-> B/P1", a, ja, b, jb); c->transform_name = "identity"; for (const auto &w : wires) c->pin_map.push_back(w); sys.connections()->add(c); } }; } // namespace TEST_CASE("diff crossing: straight P↔P / N↔N is silent") { Rig r; Pin *ap = wire(r.a, r.ja, "1", "TX_P"); Pin *an = wire(r.a, r.ja, "2", "TX_N"); Pin *bp = wire(r.b, r.jb, "1", "RX_P"); Pin *bn = wire(r.b, r.jb, "2", "RX_N"); r.bridge({{ap, bp}, {an, bn}}); app::VerifyReport vr = app::verify(&r.sys); CHECK(vr.diff_anomalies.empty()); } TEST_CASE("diff crossing: swapped legs report ONE polarity-swap anomaly") { Rig r; Pin *ap = wire(r.a, r.ja, "1", "TX_P"); Pin *an = wire(r.a, r.ja, "2", "TX_N"); Pin *bp = wire(r.b, r.jb, "1", "RX_P"); Pin *bn = wire(r.b, r.jb, "2", "RX_N"); r.bridge({{ap, bn}, {an, bp}}); // crossed on purpose app::VerifyReport vr = app::verify(&r.sys); REQUIRE(vr.diff_anomalies.size() == 1); // deduped: not once per side CHECK(vr.diff_anomalies[0].kind == AnomalyKind::DiffPolaritySwap); CHECK(vr.diff_anomalies[0].message.find("polarity swapped") != std::string::npos); } TEST_CASE("diff crossing: a single bridged leg reports incomplete") { Rig r; Pin *ap = wire(r.a, r.ja, "1", "TX_P"); wire(r.a, r.ja, "2", "TX_N"); // N leg stays local Pin *bp = wire(r.b, r.jb, "1", "RX_P"); wire(r.b, r.jb, "2", "RX_N"); // peer N leg exists, unbridged r.bridge({{ap, bp}}); app::VerifyReport vr = app::verify(&r.sys); REQUIRE(vr.diff_anomalies.size() == 1); CHECK(vr.diff_anomalies[0].kind == AnomalyKind::DiffCrossIncomplete); CHECK(vr.diff_anomalies[0].message.find("only the P legs") != std::string::npos); } TEST_CASE("diff crossing: an unsuffixed peer is not judged") { Rig r; Pin *ap = wire(r.a, r.ja, "1", "TX_P"); Pin *an = wire(r.a, r.ja, "2", "TX_N"); Pin *b1 = wire(r.b, r.jb, "1", "RXP"); // no _P/_N suffix Pin *b2 = wire(r.b, r.jb, "2", "RXN"); r.bridge({{ap, b2}, {an, b1}}); // even crossed: silent app::VerifyReport vr = app::verify(&r.sys); CHECK(vr.diff_anomalies.empty()); } TEST_CASE("diff crossing: P and N joined onto one net is flagged") { Rig r; Pin *ap = wire(r.a, r.ja, "1", "TX_P"); Pin *an = wire(r.a, r.ja, "2", "TX_N"); Pin *b1 = wire(r.b, r.jb, "1", "X"); Pin *b2 = wire(r.b, r.jb, "2", "X"); // same peer signal r.bridge({{ap, b1}, {an, b2}}); app::VerifyReport vr = app::verify(&r.sys); REQUIRE(vr.diff_anomalies.size() == 1); CHECK(vr.diff_anomalies[0].kind == AnomalyKind::DiffPolaritySwap); CHECK(vr.diff_anomalies[0].message.find("join the same net") != std::string::npos); } TEST_CASE("diff bus crossing: a dangling lane is listed, once per side") { Rig r; // Two lanes on each side; only lane 0 is bridged — lane 1 crosses nowhere. Pin *a0p = wire(r.a, r.ja, "1", "TX0_P"); Pin *a0n = wire(r.a, r.ja, "2", "TX0_N"); wire(r.a, r.ja, "3", "TX1_P"); wire(r.a, r.ja, "4", "TX1_N"); Pin *b0p = wire(r.b, r.jb, "1", "RX0_P"); Pin *b0n = wire(r.b, r.jb, "2", "RX0_N"); wire(r.b, r.jb, "3", "RX1_P"); wire(r.b, r.jb, "4", "RX1_N"); r.bridge({{a0p, b0p}, {a0n, b0n}}); app::VerifyReport vr = app::verify(&r.sys); // One aggregated anomaly per side (each names its own lane signals). REQUIRE(vr.diff_anomalies.size() == 2); for (const auto &an : vr.diff_anomalies) { CHECK(an.kind == AnomalyKind::DiffCrossIncomplete); CHECK(an.message.find("lane(s) 1 do not cross") != std::string::npos); } } TEST_CASE("diff bus crossing: a distributed bus (lanes fanned out) is silent") { // A's two lanes go to two DIFFERENT modules — legitimate backplane fan-out. System sys; Module *a = sys.modules()->merge("A"); Module *b = sys.modules()->merge("B"); Module *c = sys.modules()->merge("C"); Part *ja = new Part("J1"); a->add(ja); Part *jb = new Part("P1"); b->add(jb); Part *jc = new Part("P1"); c->add(jc); Pin *a0p = wire(a, ja, "1", "TX0_P"); Pin *a0n = wire(a, ja, "2", "TX0_N"); Pin *a1p = wire(a, ja, "3", "TX1_P"); Pin *a1n = wire(a, ja, "4", "TX1_N"); Pin *b0p = wire(b, jb, "1", "RX0_P"); Pin *b0n = wire(b, jb, "2", "RX0_N"); Pin *c0p = wire(c, jc, "1", "RX0_P"); Pin *c0n = wire(c, jc, "2", "RX0_N"); Connection *cb = new Connection("A/J1 <-> B/P1", a, ja, b, jb); cb->transform_name = "identity"; cb->pin_map.emplace_back(a0p, b0p); cb->pin_map.emplace_back(a0n, b0n); sys.connections()->add(cb); Connection *cc = new Connection("A/J1 <-> C/P1", a, ja, c, jc); cc->transform_name = "identity"; cc->pin_map.emplace_back(a1p, c0p); cc->pin_map.emplace_back(a1n, c0n); sys.connections()->add(cc); app::VerifyReport vr = app::verify(&sys); CHECK(vr.diff_anomalies.empty()); // every lane crosses somewhere } TEST_CASE("diff crossing: empty / unconnected systems are silent") { System sys; app::VerifyReport vr = app::verify(&sys); CHECK(vr.diff_anomalies.empty()); // A pair that never crosses anything: silent (local pairs are fine). Rig r; wire(r.a, r.ja, "1", "TX_P"); wire(r.a, r.ja, "2", "TX_N"); vr = app::verify(&r.sys); CHECK(vr.diff_anomalies.empty()); }