#include #include "system/analysis.hpp" #include "system/bsdl_check.hpp" #include "system/bsdl_model.hpp" #include "system/parts.hpp" #include "system/pins.hpp" #include "system/pin_spec.hpp" #include "system/system.hpp" #include "system/modules.hpp" #include "system/persist.hpp" #include #include // Minimal synthetic BSDL: 4 TAP pins, one bidir I/O, a power and a ground // linkage pin, each with a PIN_MAP ball. static const char DEMO_BSDL[] = "entity DEMO is\n" " generic (PHYSICAL_PIN_MAP : string := \"PKG\");\n" " port (\n" " TCK:in bit; TDI:in bit; TDO:out bit; TMS:in bit;\n" " IO1:inout bit;\n" " VDD:linkage bit; GND:linkage bit\n" " );\n" " attribute PIN_MAP of DEMO : entity is PHYSICAL_PIN_MAP;\n" " constant PKG : PIN_MAP_STRING :=\n" " \"TCK:A1,\" & \"TDI:A2,\" & \"TDO:A3,\" & \"TMS:A4,\" &\n" " \"IO1:B1,\" & \"VDD:C1,\" & \"GND:C2\";\n" " attribute TAP_SCAN_IN of TDI : signal is true;\n" " attribute TAP_SCAN_MODE of TMS : signal is true;\n" " attribute TAP_SCAN_OUT of TDO : signal is true;\n" " attribute TAP_SCAN_CLOCK of TCK : signal is (10.0e6, BOTH);\n" " attribute INSTRUCTION_LENGTH of DEMO : entity is 2;\n" " attribute INSTRUCTION_OPCODE of DEMO : entity is \"BYPASS (11), IDCODE (10)\";\n" " attribute IDCODE_REGISTER of DEMO : entity is \"00010010001101000101000000001111\";\n" " attribute BOUNDARY_LENGTH of DEMO : entity is 1;\n" " attribute BOUNDARY_REGISTER of DEMO : entity is \"0 (BC_1, IO1, bidir, X)\";\n" "end DEMO;\n"; TEST_CASE("BsdlModel parses a buffer into essim pin vocabulary") { BsdlModel m = BsdlModel::from_buffer(DEMO_BSDL, "demo.bsd"); REQUIRE(m.valid()); CHECK(m.entity() == "DEMO"); CHECK(m.ports().size() == 7); } TEST_CASE("apply_bsdl binds by port name and sets each pin spec") { BsdlModel m = BsdlModel::from_buffer(DEMO_BSDL, "demo.bsd"); REQUIRE(m.valid()); Part part("U1"); for (const char *n : {"TCK", "TDI", "TDO", "TMS", "IO1", "VDD", "GND"}) part.add(new Pin(n)); BsdlApplyReport r = apply_bsdl(&part, m); CHECK(r.pins_total == 7); CHECK(r.bound == 7); CHECK(r.unbound == 0); Pin *tck = part.get("TCK"); CHECK(tck->spec.function == PinFunction::JtagTck); CHECK(tck->spec.direction == PinDirection::In); CHECK(tck->spec.source == SpecSource::Bsdl); CHECK(tck->spec.pad == "A1"); CHECK(part.get("TDI")->spec.function == PinFunction::JtagTdi); CHECK(part.get("TDO")->spec.function == PinFunction::JtagTdo); CHECK(part.get("TDO")->spec.direction == PinDirection::Out); CHECK(part.get("TMS")->spec.function == PinFunction::JtagTms); Pin *io1 = part.get("IO1"); CHECK(io1->spec.function == PinFunction::Signal); CHECK(io1->spec.direction == PinDirection::Bidir); // Linkage pins classified, and the derived expected net type follows. Pin *vdd = part.get("VDD"); CHECK(vdd->spec.function == PinFunction::Power); CHECK(vdd->spec.direction == PinDirection::Power); CHECK(vdd->expected_signal_type() == SignalType::Power); Pin *gnd = part.get("GND"); CHECK(gnd->spec.function == PinFunction::Ground); CHECK(gnd->expected_signal_type() == SignalType::GndShield); } TEST_CASE("apply_bsdl falls back to the physical pad when names are balls") { BsdlModel m = BsdlModel::from_buffer(DEMO_BSDL, "demo.bsd"); REQUIRE(m.valid()); // A part whose pins are named by package ball, not by signal. Part part("U2"); for (const char *n : {"A1", "A2", "A3", "A4", "B1", "C1", "C2"}) part.add(new Pin(n)); BsdlApplyReport r = apply_bsdl(&part, m); CHECK(r.bound == 7); CHECK(r.unbound == 0); CHECK(part.get("A1")->spec.function == PinFunction::JtagTck); // TCK:A1 CHECK(part.get("C1")->spec.function == PinFunction::Power); // VDD:C1 CHECK(part.get("C2")->spec.function == PinFunction::Ground); // GND:C2 } TEST_CASE("attached BSDL path persists and re-applies on restore") { const char *bsd = "test_attach_demo.bsd"; const char *snap = "test_attach_snap.essim"; { std::ofstream o(bsd); o << DEMO_BSDL; } { System sys; Module *m = sys.modules()->merge("CARD"); Part *u = new Part("U1"); for (const char *n : {"TCK", "TDI", "TDO", "TMS", "IO1", "VDD", "GND"}) u->add(new Pin(n)); m->add(u); BsdlModel model = BsdlModel::from_file(bsd); REQUIRE(model.valid()); apply_bsdl(u, model); u->bsdl_path = bsd; std::string err; REQUIRE(save_system(&sys, snap, err)); } { std::string err; System *sys = restore_system(snap, err); REQUIRE(sys != nullptr); Part *u = sys->modules()->get("CARD")->get("U1"); CHECK(u->bsdl_path == bsd); // spec re-derived from the model at restore, not stored in the snapshot CHECK(u->get("VDD")->spec.function == PinFunction::Power); CHECK(u->get("VDD")->spec.source == SpecSource::Bsdl); CHECK(u->get("VDD")->expected_signal_type() == SignalType::Power); CHECK(u->get("TCK")->spec.function == PinFunction::JtagTck); CHECK(u->get("TCK")->spec.pad == "A1"); delete sys; } std::remove(bsd); std::remove(snap); } TEST_CASE("check_bsdl_completeness flags a device power pin absent from the part") { const char *bsd = "test_complete_demo.bsd"; { std::ofstream o(bsd); o << DEMO_BSDL; } System sys; Module *m = sys.modules()->merge("CARD"); Part *u = new Part("U1"); // All pins EXCEPT VDD (a power port at ball C1) → its port is unmatched. for (const char *n : {"TCK", "TDI", "TDO", "TMS", "IO1", "GND"}) u->add(new Pin(n)); u->bsdl_path = bsd; m->add(u); auto a = check_bsdl_completeness(&sys); REQUIRE(a.size() == 1); CHECK(a[0].kind == AnomalyKind::BsdlPinMissing); // With VDD present (by ball or by name), no completeness issue. u->add(new Pin("VDD")); CHECK(check_bsdl_completeness(&sys).empty()); std::remove(bsd); }