Files
essim/tests/test_bsdl_apply.cpp
François 63ca17d048 build: split core/ from frontends/; prepare for multiple GUI/TUI targets
Reorganise the tree into business vs frontend as separate directories:
  src/core/{domain,imports,app}   (was system/, imports/, app/)
  src/frontends/tui/              (was tui/ + main.cpp)
  tests/tui/                      (the FTXUI-coupled helper test)
All cross-dir #include paths rewritten; same-dir includes untouched.

CMake: essim_core is the frontend-agnostic business library — links libzip,
pugixml and bsdl, NO GUI toolkit. Each frontend is a self-contained
src/frontends/<name>/ (own CMakeLists, toolkit, main.cpp) that links
essim_core, selected with -DESSIM_FRONTEND=<name> (default tui; 'none' = core +
tests only, no toolkit fetched). FTXUI moved into the tui frontend. Tests are
split: essim_tests links essim_core (no FTXUI), essim_tui_tests links essim_tui.

Verified: default tui build green (ctest 2/2); ESSIM_FRONTEND=none builds the
core + tests with FTXUI never fetched and no `essim` binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 19:33:06 +02:00

170 lines
6.1 KiB
C++

#include <doctest/doctest.h>
#include "core/domain/analysis.hpp"
#include "core/domain/bsdl_check.hpp"
#include "core/domain/bsdl_model.hpp"
#include "core/domain/parts.hpp"
#include "core/domain/pins.hpp"
#include "core/domain/pin_spec.hpp"
#include "core/domain/system.hpp"
#include "core/domain/modules.hpp"
#include "core/domain/persist.hpp"
#include <cstdio>
#include <fstream>
// 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);
}