TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
rule_engine.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// Resolves KiCad constraints for pairs of copper items (design doc 03 §6): net-class clearances, local pad
4// overrides, board minimums and custom .kicad_dru rules with their conditions.
5#include <tuple>
6#include <mutex>
7#include <map>
8#include <memory>
9#include <optional>
10#include <string>
11#include <string_view>
12#include <vector>
13
14#include "drc/copper.hpp"
15#include "drc/rule_geometry.hpp"
16#include "model/board.hpp"
17#include "model/rules.hpp"
18
19namespace tmk::drc {
20
21class Condition; // compiled custom-rule condition
22
23// A differential-pair net name as KiCad 10.0.6 reads it (DRC_ENGINE::MatchDpSuffix): from the end, digits and underscores are
24// skipped, then P or + makes the positive half and N or - the negative one. `complement` is the other half's name and `base` the name
25// before the polarity character, so D_P1 pairs with D_N1 on base "D_". Polarity 0: not a pair name.
26struct DpSuffix {
27 int polarity = 0;
28 std::string complement, base;
29};
30DpSuffix match_dp_suffix(std::string_view name);
31
33 public:
36
37 // Required copper-to-copper clearance between two items on a copper layer.
38 Coord clearance(const CopperItem& a, const CopperItem& b, int layer) const;
39 // Required clearance between a hole and a copper item.
40 Coord hole_clearance(const CopperItem* hole_owner, const CopperItem& other, int layer) const;
41 Coord hole_to_hole(const CopperItem* a, const CopperItem* b) const;
42 Coord edge_clearance(const CopperItem& a, int layer) const;
43 // Minimum and maximum track width (max = 0: none).
44 std::pair<Coord, Coord> track_width(const CopperItem& t, int layer) const;
45 // The severity of the last rule with a track_width constraint that matches the track ("" when none matches or it sets none): in KiCad
46 // that rule decides the track's width check, so an ignored rule hides the board minimum too (tuning profiles, doc 05 §58).
47 std::string track_width_severity(const CopperItem& t, int layer) const;
50 Coord hole_size_min(const CopperItem* owner) const;
51 // Largest clearance any pair could need (for spatial-index inflation).
52 Coord max_clearance() const { return max_clearance_; }
53 // KiCad applies a copper zone's own clearance to its fill (ZONE::GetLocalClearance): the larger of it and the
54 // net-class clearance, unless a pad override or custom rule decides. The DRC enables it; the router does not
55 // yet (D51: changing it needs a routing benchmark run).
57
58 const model::NetClass& netclass(const CopperItem& it) const;
59 // True if the two nets are the P/N (or +/-) halves of one differential pair.
61 // Length constraint (min, max) of the last custom rule whose condition matches a track of `net`, if any.
62 std::pair<std::optional<Coord>, std::optional<Coord>> length_constraint(model::NetId net) const;
63 // Maximum skew of the last custom rule with a `skew` constraint matching a track of `net`, if any.
64 std::optional<Coord> skew_constraint(model::NetId net) const;
65 // Last custom constraint of `type` (e.g. diff_pair_gap, diff_pair_uncoupled) whose rule matches a track of `net`.
66 std::optional<model::Constraint> net_constraint(model::NetId net, const std::string& type) const;
67 // KiCad's matched-length check groups items by the rule that gives them a constraint (DRC_ENGINE::EvalRules'
68 // parent rule; doc 05 §59): the position in rule order of the last rule with a `type` constraint matching `it` on
69 // `layer`, or -1. rule(i) is that rule.
70 int last_rule_with(const CopperItem& it, int layer, const std::string& type) const;
71 const model::CustomRule& rule(int i) const { return *rules_[static_cast<std::size_t>(i)].rule; }
72 // The same for a net as a whole (a track of `net` on any layer), as length_constraint and net_constraint see it.
73 int net_rule_with(model::NetId net, const std::string& type) const;
74 // The other half of `net`'s differential pair (KiCad's DpCoupledNet), or 0.
76 return static_cast<std::size_t>(net) < dp_partner_.size() ? dp_partner_[static_cast<std::size_t>(net)] : 0;
77 }
78 // True when `net` is the positive half of a pair (P or +, match_dp_suffix); only meaningful when dp_partner(net) is set.
79 bool dp_positive(model::NetId net) const {
80 return static_cast<std::size_t>(net) < dp_positive_.size() && dp_positive_[static_cast<std::size_t>(net)];
81 }
82
83 // Name of the last rule disallowing `it` on `layer`, if any (KiCad: items_not_allowed).
84 std::optional<std::string> disallowed(const CopperItem& it, int layer) const;
85 // Router fast paths for a new track or via of `net`, from rules whose conditions do not depend on the
86 // item's position, size or footprint (per-net layer and via-type masks).
87 bool track_allowed(model::NetId net, int layer) const;
88 // `Blind` router probes have no span yet, so either blind/buried keyword conservatively forbids them.
89 // Board items are distinguished exactly by whether their span touches an outer copper layer.
91 // A disallow rule on tracks or vias depends on position, size or footprint: the router asks probe_disallowed.
92 bool positional_disallow() const { return positional_disallow_; }
93 // A new track or via (index -1, actual shape, width and layers; a via's layers are its span, which tells
94 // blind from buried) is disallowed on one of its layers, every custom rule applied in order as in disallowed().
96 // Hole-to-copper clearance on `layer`, any net; -1 when no rule matches (KiCad: hole_clearance).
97 Coord physical_hole_clearance(const CopperItem* hole_owner, const CopperItem& other, int layer) const;
98 bool any_physical_hole_clearance() const { return max_physical_hole_ > 0; }
99 // True when a physical_hole_clearance rule holds between a via of `net` and a track of the same net: the
100 // tracks that end in a via touch its hole, so every via of that net would break the rule.
102 Coord max_physical_hole_clearance() const { return max_physical_hole_; }
103 // Hole-to-copper clearance the router keeps between a hole and other nets' copper: the board minimum raised to
104 // every applied custom hole_clearance rule. The router's checks and caches have no per-pair hole rule, so a rule
105 // with a condition or a layer is kept everywhere (conservative, rule 6; a warning says so). The DRC stays exact
106 // (hole_clearance above).
107 Coord router_hole_clearance() const { return router_hole_; }
108 // Custom rules that the per-class obstacle cache cannot represent require exact per-point checks.
109 bool needs_exact_routing() const { return needs_exact_; }
110 const std::vector<std::string>& warnings() const { return warnings_; }
111
112 private:
113 // Keyword bits of a rule's disallow constraints.
114 enum : std::uint16_t {
115 kTrack = 1, kVia = 2, kThroughVia = 4, kMicroVia = 8, kBlindVia = 16, kBuriedVia = 32, kPad = 64, kZone = 128, kGraphic = 256,
116 kRouted = kTrack | kVia | kThroughVia | kMicroVia | kBlindVia | kBuriedVia,
117 };
118 struct Compiled {
119 const model::CustomRule* rule;
120 std::unique_ptr<Condition> cond; // null = always
121 bool valid = true;
122 bool positional = false; // position, size, footprint or pad condition: evaluated per item, not per net
123 std::uint16_t words = 0; // disallow keywords
124 };
125 bool words_match(std::uint16_t words, const CopperItem& it, const model::ViaType* via_type, bool span) const;
126 // Item type, layer and condition all match a disallow constraint.
127 bool disallow_hit(const Compiled& c, const CopperItem& it, int layer, const model::ViaType* via_type = nullptr, bool span = false) const;
128 // Value of the last matching custom constraint of `type` (min field), trying (a,b) and (b,a).
129 std::optional<Coord> custom_min(const char* type, const CopperItem* a, const CopperItem* b, int layer) const;
130 bool layer_matches(const std::string& sel, int layer) const;
131
132 const model::Board& b_;
133 const model::DesignRules& r_;
134 std::vector<Compiled> rules_;
135 std::vector<std::string> warnings_;
136 Coord max_clearance_ = 0;
137 std::vector<const model::NetClass*> net_class_; // by net id (nets created later fall back to a lookup)
138 std::vector<model::NetId> dp_partner_; // by net id: the other half of a P/N pair, or 0
139 std::vector<char> dp_positive_; // by net id: the positive half of a pair
140 std::vector<std::string> dp_base_; // by net id: the pair's base name (inDiffPair), for pair nets
141 std::vector<std::vector<std::string>> net_classes_; // by net id: DesignRules::classes_of
142 // Area and courtyard functions of a zone fill, by (condition node, zone, fill): a fill has tens of thousands
143 // of points and is asked again for every pair it is in (vme-wren: 80 s of a DRC).
144 mutable std::map<std::tuple<const void*, int, int>, bool> area_cache_;
145 mutable std::mutex area_mutex_;
146 CourtyardCache courtyards_;
147 std::vector<geom::Box> zone_box_; // outline box of every zone, for area functions
148 bool positional_disallow_ = false;
149 bool any_custom_clearance_ = false;
150 bool zone_overrides_ = false;
151 bool needs_exact_ = false;
152 Coord max_physical_hole_ = 0;
153 Coord router_hole_ = 0;
154 friend class Condition;
155};
156
157} // namespace tmk::drc
The board model (design doc 02 §3): every object TraceMaker reads from a .kicad_pcb,...
bool track_allowed(model::NetId net, int layer) const
Router fast paths for a new track or via of net, from rules whose conditions do not depend on the ite...
Coord max_clearance() const
Largest clearance any pair could need (for spatial-index inflation).
bool needs_exact_routing() const
Custom rules that the per-class obstacle cache cannot represent require exact per-point checks.
std::optional< std::string > disallowed(const CopperItem &it, int layer) const
Name of the last rule disallowing it on layer, if any (KiCad: items_not_allowed).
bool via_allowed(model::NetId net, model::ViaType type=model::ViaType::Through) const
Blind router probes have no span yet, so either blind/buried keyword conservatively forbids them.
bool via_hole_rule_hits_own_tracks(model::NetId net) const
True when a physical_hole_clearance rule holds between a via of net and a track of the same net: the ...
void use_zone_clearance_overrides()
KiCad applies a copper zone's own clearance to its fill (ZONE::GetLocalClearance): the larger of it a...
int net_rule_with(model::NetId net, const std::string &type) const
The same for a net as a whole (a track of net on any layer), as length_constraint and net_constraint ...
const std::vector< std::string > & warnings() const
model::NetId dp_partner(model::NetId net) const
The other half of net's differential pair (KiCad's DpCoupledNet), or 0.
Coord hole_clearance(const CopperItem *hole_owner, const CopperItem &other, int layer) const
Required clearance between a hole and a copper item.
Coord max_physical_hole_clearance() const
bool positional_disallow() const
A disallow rule on tracks or vias depends on position, size or footprint: the router asks probe_disal...
std::pair< Coord, Coord > track_width(const CopperItem &t, int layer) const
Minimum and maximum track width (max = 0: none).
std::optional< Coord > skew_constraint(model::NetId net) const
Maximum skew of the last custom rule with a skew constraint matching a track of net,...
Coord annular_width_min(const CopperItem &v) const
bool coupled_diff_pair(model::NetId a, model::NetId b) const
True if the two nets are the P/N (or +/-) halves of one differential pair.
const model::CustomRule & rule(int i) const
std::pair< std::optional< Coord >, std::optional< Coord > > length_constraint(model::NetId net) const
Length constraint (min, max) of the last custom rule whose condition matches a track of net,...
Coord clearance(const CopperItem &a, const CopperItem &b, int layer) const
Required copper-to-copper clearance between two items on a copper layer.
bool any_physical_hole_clearance() const
Coord router_hole_clearance() const
Hole-to-copper clearance the router keeps between a hole and other nets' copper: the board minimum ra...
Coord physical_hole_clearance(const CopperItem *hole_owner, const CopperItem &other, int layer) const
Hole-to-copper clearance on layer, any net; -1 when no rule matches (KiCad: hole_clearance).
Coord hole_size_min(const CopperItem *owner) const
std::optional< model::Constraint > net_constraint(model::NetId net, const std::string &type) const
Last custom constraint of type (e.g. diff_pair_gap, diff_pair_uncoupled) whose rule matches a track o...
bool probe_disallowed(const CopperItem &it, model::ViaType type=model::ViaType::Through) const
A new track or via (index -1, actual shape, width and layers; a via's layers are its span,...
Coord hole_to_hole(const CopperItem *a, const CopperItem *b) const
std::string track_width_severity(const CopperItem &t, int layer) const
The severity of the last rule with a track_width constraint that matches the track ("" when none matc...
int last_rule_with(const CopperItem &it, int layer, const std::string &type) const
KiCad's matched-length check groups items by the rule that gives them a constraint (DRC_ENGINE::EvalR...
Coord edge_clearance(const CopperItem &a, int layer) const
RuleEngine(const model::Board &b, const model::DesignRules &r)
bool dp_positive(model::NetId net) const
True when net is the positive half of a pair (P or +, match_dp_suffix); only meaningful when dp_partn...
Coord via_diameter_min(const CopperItem &v) const
const model::NetClass & netclass(const CopperItem &it) const
Copper items with exact shapes, built from the board model, for DRC and routing obstacles.
DpSuffix match_dp_suffix(std::string_view name)
std::int32_t NetId
0 = no net
Definition board.hpp:21
std::int64_t Coord
Engine coordinate: signed 64-bit nanometres.
Definition units.hpp:12
Positional custom-rule predicates.
Design rules as KiCad defines them (design doc 08 §3–4): board minimums and net classes from the ....
compiled custom-rule condition
std::string complement