#include "bsdl_check.hpp" #include "modules.hpp" #include "nets.hpp" #include "parts.hpp" #include "pin_spec.hpp" #include "pins.hpp" #include "signals.hpp" #include "system.hpp" #include #include #include #include namespace { std::string part_label(const Part *p) { std::string mod = (p && p->prnt) ? p->prnt->name : std::string("?"); return mod + "/" + (p ? p->name : std::string("?")); } std::string pin_label(const Pin *p) { std::string part = (p && p->prnt) ? p->prnt->name : std::string("?"); return part + "/" + (p ? p->name : std::string("?")); } std::string join_labels(const std::vector &pins) { std::string s; for (const Pin *p : pins) s += (s.empty() ? "" : ", ") + pin_label(p); return s; } } // namespace std::vector check_pin_specs(System *sys) { std::vector out; if (!sys) return out; for (const Net &net : compute_all_nets(sys)) { std::vector pins; std::vector sigs; Module *mod = nullptr; std::string label; for (const auto &mp : net.members) { if (!mod) mod = mp.first; sigs.push_back(mp.second); if (label.empty() && mp.first && mp.second) label = mp.first->name + "/" + mp.second->name; for (auto &kv : *mp.second) pins.push_back(kv.second); } if (pins.size() < 2) continue; // singleton net — nothing to compare int outs = 0, drivers = 0, ins = 0, known = 0; std::vector out_pins, in_pins, nc_pins; for (Pin *p : pins) { const PinSpec &s = p->spec; if (s.function == PinFunction::NoConnect) nc_pins.push_back(p); if (s.direction == PinDirection::Unknown) continue; ++known; switch (s.direction) { case PinDirection::Out: ++outs; ++drivers; out_pins.push_back(p); break; case PinDirection::Bidir: ++drivers; break; case PinDirection::Power: ++drivers; break; case PinDirection::In: ++ins; in_pins.push_back(p); break; default: break; } } // A no-connect pin that is nonetheless on a multi-pin net. for (Pin *p : nc_pins) { Anomaly a; a.kind = AnomalyKind::NcWired; a.module = mod; a.involved = sigs; a.message = pin_label(p) + " is marked no-connect but wired to net " + label; out.push_back(std::move(a)); } if (!known) continue; // no direction data on this net — skip drive checks if (outs >= 2) { Anomaly a; a.kind = AnomalyKind::DriveContention; a.module = mod; a.involved = sigs; a.message = "net " + label + ": " + std::to_string(outs) + " output drivers (" + join_labels(out_pins) + ")"; out.push_back(std::move(a)); } if (ins >= 1 && drivers == 0) { Anomaly a; a.kind = AnomalyKind::UndrivenNet; a.module = mod; a.involved = sigs; a.message = "net " + label + ": input(s) with no driver (" + join_labels(in_pins) + ")"; out.push_back(std::move(a)); } } return out; } std::vector check_jtag_chain(System *sys) { std::vector out; if (!sys) return out; // Map every pin to the index of the net it sits on. std::vector nets = compute_all_nets(sys); std::unordered_map net_of; for (size_t i = 0; i < nets.size(); ++i) for (auto &mp : nets[i].members) for (auto &kv : *mp.second) net_of[kv.second] = (int)i; auto net_id = [&](Pin *p) -> int { if (!p) return -1; auto it = net_of.find(p); return it == net_of.end() ? -1 : it->second; }; // Collect TAP devices: any part carrying ≥1 pin with a TAP function. struct Tap { Part *part = nullptr; Pin *tdi = nullptr, *tdo = nullptr, *tms = nullptr, *tck = nullptr, *trst = nullptr; }; std::vector taps; for (auto &mkv : *sys->modules()) for (auto &pkv : *mkv.second) { Tap t; t.part = pkv.second; bool any = false; for (auto &nkv : *t.part) { Pin *pin = nkv.second; switch (pin->spec.function) { case PinFunction::JtagTdi: t.tdi = pin; any = true; break; case PinFunction::JtagTdo: t.tdo = pin; any = true; break; case PinFunction::JtagTms: t.tms = pin; any = true; break; case PinFunction::JtagTck: t.tck = pin; any = true; break; case PinFunction::JtagTrst: t.trst = pin; any = true; break; default: break; } } if (any) taps.push_back(t); } if (taps.empty()) return out; // (1) every TAP device needs the four mandatory signals. for (const Tap &t : taps) { std::string miss; auto need = [&](Pin *p, const char *n) { if (!p) miss += (miss.empty() ? "" : ", ") + std::string(n); }; need(t.tdi, "TDI"); need(t.tdo, "TDO"); need(t.tms, "TMS"); need(t.tck, "TCK"); if (!miss.empty()) { Anomaly a; a.kind = AnomalyKind::JtagTapIncomplete; a.module = t.part ? t.part->prnt : nullptr; a.message = part_label(t.part) + ": incomplete TAP, missing " + miss; out.push_back(std::move(a)); } } // (2) TMS and TCK must each be one common net across all TAP devices. auto check_bus = [&](const char *name, const std::vector> &pins) { if (pins.size() < 2) return; int ref = -1; for (const auto &pp : pins) { int n = net_id(pp.second); if (n >= 0) { ref = n; break; } } if (ref < 0) { Anomaly a; a.kind = AnomalyKind::JtagBusUnbridged; a.message = std::string(name) + ": no TAP device has a connected " + name; out.push_back(std::move(a)); return; } std::string off; for (const auto &pp : pins) if (net_id(pp.second) != ref) off += (off.empty() ? "" : ", ") + part_label(pp.first); if (!off.empty()) { Anomaly a; a.kind = AnomalyKind::JtagBusUnbridged; a.message = std::string(name) + " is not common to all TAP devices (off-bus: " + off + ")"; out.push_back(std::move(a)); } }; std::vector> tms_pins, tck_pins; for (const Tap &t : taps) { if (t.tms) tms_pins.push_back({t.part, t.tms}); if (t.tck) tck_pins.push_back({t.part, t.tck}); } check_bus("TMS", tms_pins); check_bus("TCK", tck_pins); // (3) TDO→TDI daisy chain: a link is two devices sharing a net (A.tdo, B.tdi). const int n = (int)taps.size(); std::vector downstream(n, 0), upstream(n, 0); for (int i = 0; i < n; ++i) { if (taps[i].tdo && net_id(taps[i].tdo) < 0) { Anomaly a; a.kind = AnomalyKind::JtagChainBreak; a.module = taps[i].part ? taps[i].part->prnt : nullptr; a.message = part_label(taps[i].part) + "/TDO is not connected to any net"; out.push_back(std::move(a)); } if (taps[i].tdi && net_id(taps[i].tdi) < 0) { Anomaly a; a.kind = AnomalyKind::JtagChainBreak; a.module = taps[i].part ? taps[i].part->prnt : nullptr; a.message = part_label(taps[i].part) + "/TDI is not connected to any net"; out.push_back(std::move(a)); } } for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) { if (i == j) continue; int a = net_id(taps[i].tdo), b = net_id(taps[j].tdi); if (a >= 0 && a == b) { ++downstream[i]; ++upstream[j]; } } int heads = 0, tails = 0; for (int i = 0; i < n; ++i) { if (downstream[i] > 1) { Anomaly a; a.kind = AnomalyKind::JtagChainBreak; a.module = taps[i].part ? taps[i].part->prnt : nullptr; a.message = part_label(taps[i].part) + "/TDO drives " + std::to_string(downstream[i]) + " TDIs (chain fan-out)"; out.push_back(std::move(a)); } if (upstream[i] > 1) { Anomaly a; a.kind = AnomalyKind::JtagChainBreak; a.module = taps[i].part ? taps[i].part->prnt : nullptr; a.message = part_label(taps[i].part) + "/TDI is driven by " + std::to_string(upstream[i]) + " TDOs"; out.push_back(std::move(a)); } if (taps[i].tdi && net_id(taps[i].tdi) >= 0 && upstream[i] == 0) ++heads; if (taps[i].tdo && net_id(taps[i].tdo) >= 0 && downstream[i] == 0) ++tails; } if (n > 1 && (heads != 1 || tails != 1)) { Anomaly a; a.kind = AnomalyKind::JtagChainBreak; a.message = "JTAG chain is not a single daisy chain: " + std::to_string(heads) + " head(s), " + std::to_string(tails) + " tail(s) over " + std::to_string(n) + " TAP device(s)"; out.push_back(std::move(a)); } return out; }