#include #include "system/analysis.hpp" #include "system/bsdl_check.hpp" #include "system/modules.hpp" #include "system/parts.hpp" #include "system/pin_spec.hpp" #include "system/pins.hpp" #include "system/signals.hpp" #include "system/system.hpp" #include #include namespace { Pin *mkpin(Part *part, const char *name, PinDirection d, PinFunction f) { Pin *p = new Pin(name); p->spec.direction = d; p->spec.function = f; p->spec.source = SpecSource::Bsdl; part->add(p); return p; } void wire(Module *m, const char *signame, std::vector pins) { Signal *s = m->signals->merge(signame); for (Pin *p : pins) { s->add(p); p->connect(s); } } int count_kind(const std::vector &v, AnomalyKind k) { return (int)std::count_if(v.begin(), v.end(), [&](const Anomaly &a) { return a.kind == k; }); } } // namespace TEST_CASE("check_pin_specs flags contention, undriven nets and NC-wired pins") { System sys; Module *m = sys.modules()->merge("M"); Part *u1 = new Part("U1"); Part *u2 = new Part("U2"); m->add(u1); m->add(u2); // Two push-pull outputs on one net → contention. wire(m, "NET_CONT", {mkpin(u1, "O1", PinDirection::Out, PinFunction::Signal), mkpin(u2, "O2", PinDirection::Out, PinFunction::Signal)}); // Two inputs, no driver → undriven. wire(m, "NET_UNDR", {mkpin(u1, "I1", PinDirection::In, PinFunction::Signal), mkpin(u2, "I2", PinDirection::In, PinFunction::Signal)}); // A no-connect pin wired to a driver → nc-wired (and not undriven: the // NC pin is Power-direction here, so the net has a driver). wire(m, "NET_NC", {mkpin(u1, "NC1", PinDirection::Power, PinFunction::NoConnect), mkpin(u2, "D1", PinDirection::Out, PinFunction::Signal)}); // Healthy: one driver + one input → no anomaly. wire(m, "NET_OK", {mkpin(u1, "OD", PinDirection::Out, PinFunction::Signal), mkpin(u2, "ID", PinDirection::In, PinFunction::Signal)}); auto anoms = check_pin_specs(&sys); CHECK(count_kind(anoms, AnomalyKind::DriveContention) == 1); CHECK(count_kind(anoms, AnomalyKind::UndrivenNet) == 1); CHECK(count_kind(anoms, AnomalyKind::NcWired) == 1); } TEST_CASE("check_pin_specs stays silent on un-modelled nets") { System sys; Module *m = sys.modules()->merge("M"); Part *u1 = new Part("U1"); Part *u2 = new Part("U2"); m->add(u1); m->add(u2); // No specs set (direction Unknown / function Unknown) → no findings. Pin *a = new Pin("A"); Pin *b = new Pin("B"); u1->add(a); u2->add(b); wire(m, "NET", {a, b}); auto anoms = check_pin_specs(&sys); CHECK(anoms.empty()); } namespace { // Add the four mandatory TAP pins to a part and return it. Part *mk_tap(Module *m, const char *name) { Part *p = new Part(name); mkpin(p, "TDI", PinDirection::In, PinFunction::JtagTdi); mkpin(p, "TDO", PinDirection::Out, PinFunction::JtagTdo); mkpin(p, "TMS", PinDirection::In, PinFunction::JtagTms); mkpin(p, "TCK", PinDirection::In, PinFunction::JtagTck); m->add(p); return p; } } // namespace TEST_CASE("check_jtag_chain accepts a healthy daisy chain") { System sys; Module *m = sys.modules()->merge("M"); Part *u1 = mk_tap(m, "U1"); Part *u2 = mk_tap(m, "U2"); Part *u3 = mk_tap(m, "U3"); wire(m, "TCK", {u1->get("TCK"), u2->get("TCK"), u3->get("TCK")}); wire(m, "TMS", {u1->get("TMS"), u2->get("TMS"), u3->get("TMS")}); wire(m, "TDI_HEAD", {u1->get("TDI")}); // from the controller wire(m, "L1", {u1->get("TDO"), u2->get("TDI")}); // U1 → U2 wire(m, "L2", {u2->get("TDO"), u3->get("TDI")}); // U2 → U3 wire(m, "TDO_TAIL", {u3->get("TDO")}); // back to the controller CHECK(check_jtag_chain(&sys).empty()); } TEST_CASE("check_jtag_chain flags a broken chain and a split bus") { System sys; Module *m = sys.modules()->merge("M"); Part *u1 = mk_tap(m, "U1"); Part *u2 = mk_tap(m, "U2"); Part *u3 = mk_tap(m, "U3"); wire(m, "TCK", {u1->get("TCK"), u2->get("TCK"), u3->get("TCK")}); // U3's TMS is on its own net → not bridged to the TMS bus. wire(m, "TMS", {u1->get("TMS"), u2->get("TMS")}); wire(m, "TMS_ALT", {u3->get("TMS")}); // Chain link U2 → U3 is missing: U2.TDO and U3.TDI dangle. wire(m, "TDI_HEAD", {u1->get("TDI")}); wire(m, "L1", {u1->get("TDO"), u2->get("TDI")}); wire(m, "U2_TDO", {u2->get("TDO")}); wire(m, "U3_TDI", {u3->get("TDI")}); wire(m, "TDO_TAIL", {u3->get("TDO")}); auto a = check_jtag_chain(&sys); CHECK(count_kind(a, AnomalyKind::JtagChainBreak) >= 1); CHECK(count_kind(a, AnomalyKind::JtagBusUnbridged) >= 1); } TEST_CASE("check_jtag_chain reports an incomplete TAP") { System sys; Module *m = sys.modules()->merge("M"); Part *u1 = new Part("U1"); // only TDI + TDO, no TMS/TCK mkpin(u1, "TDI", PinDirection::In, PinFunction::JtagTdi); mkpin(u1, "TDO", PinDirection::Out, PinFunction::JtagTdo); m->add(u1); auto a = check_jtag_chain(&sys); CHECK(count_kind(a, AnomalyKind::JtagTapIncomplete) == 1); } TEST_CASE("check_source_conflicts flags a BSDL rail left unconnected") { System sys; Module *m = sys.modules()->merge("M"); Part *u = new Part("U1"); m->add(u); // A BSDL power pin with no signal → conflict (a rail floated in the netlist). Pin *vcc = new Pin("VCC"); vcc->spec.function = PinFunction::Power; vcc->spec.source = SpecSource::Bsdl; u->add(vcc); // A BSDL ground pin that IS connected → no conflict. Pin *gnd = mkpin(u, "GND", PinDirection::Power, PinFunction::Ground); wire(m, "GNDNET", {gnd, mkpin(u, "X", PinDirection::Out, PinFunction::Signal)}); auto a = check_source_conflicts(&sys); CHECK(count_kind(a, AnomalyKind::SourceConflict) == 1); }