drc_query: query() based, scriptable Design Rule Checker

The drc_query plugin is a glue layer between the query plugin and the DRC infrastructure. It allows the user (and sch import flows) to script DRC checks and use these script as part of the normal DRC workflow. This chapter describes the how to configure and use query() based DRC scripts and offers a tutorial on developing DRC scripts.

Configuration

DRC rules are specified as a list of hash nodes under the plugins/drc_query/rules config path, from the config source of the user's choice. Commonly used config sources:

Example 1: a full example of an user config, saved as ~/.pcb-rnd/drc_query.conf:

li:pcb-rnd-conf-v1 {
  ha:overwrite {
    ha:plugins {
      ha:drc_query {
        li:definitions {
          ha:min_drill {
            type = coord
            desc = {minimum drill diameter}
            default = 0
          }
        }
        li:rules {
          ha:hole_dia {
            type = single hole
            title = hole too small
            desc = padstack hole diameter is too small
            disable = 0
            query = {(@.hole > 0) && (@.hole < $min_drill)}
          }
        }
      }
    }
  }
}

There are two main subtrees below ha:drc_query: definitions and rules.

Definitions

Definitions create new config nodes under design/drc. These config nodes can be used to configure parameters of the drc check. The order of definitions does not matter but the name of each definition must be unique.

A definition is a named hash subtree with the following fields:

name mandatory meaning
type yes data type of the configuration; one of: coord, boolean, integer, real, unit, color
desc no human readable description
default no low priority default value (format depends on type) for the case the configuration node is not set
legacy no reserved for stock rules for compatibility with the old DRC, do not use in new rules

The definition in the above example creates config node design/drc/min_drill. This new config node is accessible through the config system normally, through the conf() action or the GUI (preferences dialog). From query(), it can be referenced using the shorthand $min_drill form.

Rules

Text fields type, title and desc are optional user readable strings that will be presented in the DRC report. The type::title pair serves as an unique identifier for the rule. If optional disable is 'yes' or 'true' or a non-zero integer value, the rule is temporarily disabled.

Note: since drc errors are presented on a per type basis, and drc rules are executed each in its own, independent context, the order of the hash nodes within the rules list (merged from different config sources) determines the order in which tests are performed, but otherwise does not matter (as tests are independent).

Rules optionally can have a source field, which is a one word free form text, indicating where the rule is coming from. User specified/configured rules should leave this field empty. Rules imported from the netlist (or schematics) should set this field to netlist. When present, the GUI will display rules from the same source grouped. But the real purpose of the source field is to make re-import of rules possible: when a new import from the same source is performed, the import code should remove all rules coming from the source and create new rules with the same source.

Query text may consists of multiple lines. The format is as described at the query plugin. There are two alternate forms:

Example 2: a rule based query script example is finding overlapping drilled holes:

query = {
  rule overlap
  let A @.type==PSTK
  let B A
  assert (A.ID > B.ID) && (distance(A.x, A.y, B.x, B.y) < (A.hole + B.hole)/2) thus violation(DRCGRP1, A, DRCGRP2, B)
}

Note: since drc errors are presented on a per type basis, and drc rules are executed each in its own, independent context, the order of rules does not matter.

Stock DRC scripts

The default drc_query config shipped with pcb-rnd contains a few stock DRC rules. These are the most essential rules that most boards will need. (Users are encouraged to download further scripts to extend the DRC system.) This chapter documents the stock DRC scripts and the checks they perform.

hole_dia

Lists holes with diameter smaller than the board-global minimum (set in $min_drill). Note: only round holes (hole diameter) are checked, which are always in padstacks. Slots (in padstacks) or mech/boundary layer explicit object are not checked.

hole_overlap

Checks pairs of holes (as defined for hole_dia above) and list pairs that have any overlap between their drilled hole. (Some fabs may refuse to drill such holes because of increased risk of breaking drill bits.)

net_break

Check every overlapping pair of copper objects within known nets. Throw a DRC violation if the overlap offset is smaller than $min_copper_overlap. (Although not very likely with modern CAM and fab processes, but insufficient overlap may cause glitches or even broken net.)

net_short

Check every pair of copper objects within known nets. Throw a DRC violation if two objects of different nets have non-copper gap less than $min_copper_clearance.

min_copper_thickness

Check each copper object with thickness (arcs and lines) and throw a violation for those that are thicker than 0 but thinner than $min_copper_thickness. (The exception for 0 is provided for compatibility. Please use the noexport mechanism instead.)

min_silk_thickness

Same as min_copper_thickness but runs on silk objects and uses $min_silk_thickness.

beyond_drawing_area

List any object whose bounding box is fully beyond the drawing area in any direction. This is useful for detecting copy&paste or object move leftovers, especially if the leftover is a small object.

min_ring

For each padstack calculate the neck size, which is the shortest straight line that can be drawn from the inner (hole/slot) contour and the outer (copper) contour of the padstack. If the neck size is below $min_ring, throw a violation.

fullpoly

Lists any polygon that has the FULLPOLY flag and indeed has multiple islands. The fullpoly feature is provided only for compatibility and should not be used: it may break the DRC and connection finding in some common cases.

ko_named

Courtyard/keepout: warn for pairs of objects that overlap on layer groups with purpose string starting with "ko.". The most common use is part mechanical courtyard. Please refer to the ko_id pool node for detailed documentation.

invalid_polygons

The polygon lib of pcb-rnd requires that polygon contours to be not self intersecting. When pcb-rnd is configured with --debug, a lot of checks are ran runtime and the code asserts/aborts as early as possible when detecting invalid polygon contour. However, in production compilation these checks are disabled. The only way an user of a production pcb-rnd can find all invalid polygons on a board is running this test. Note: invalid polygons will eventually break something on the board: the on-screen render, an export render or polygon clipping.

Tutorial

Single expression scripts

In Example 1, a single query() expression is used: it iterates through all objects available (@) and evaluates whether the object's hole property is smaller than a predefined value. This expression can result in three different outcome for any object:

Such simple, single-expression DRC rules are common for checks that need to decide whether every single object matching some criteria passes a test on its properties. It works for Example 1 because each padstack object can be checked separately from any other object and the outcome of a check depends only on properties of the single padstack object and constants. There is only one iterator used, @, which iterates on all objects of the board.

Note: the left-side check of && for hole diameter greater than zero is needed because of the pcb-rnd data model: hole diameter zero means no hole (e.g. smd pads) - no warning should be issued for that.

Rule scripts

However, in some situations a DRC violation depends on two or more objects, thus iterations need to be done on pairs (or tuples) of objects. It is done by:

Example 2 demonstrates a simple case of pair-wise iteration: first a list called A is set up; it will contain all objects of the board whose type is PSTK. Then list A is copied to B in the second let.

Note: the second argument of a let is always an expression. In the first case it is more trivial: (@.type==PSTK). When this expression is true, the last evaluated object (the current iteration with @) is appended to the destination list (specified in the first argument). The second let "copies" the list by expression (A). It really means: having the iterator run on each item of A, evaluating each; they will all be true (since they are existing objects); once an item is true, the last evaluated object (the same that caused the expression to be true) is appended to the left side-list B. The part that may be confusing is this: on the left side, there's a list name, on the right side there is an expression. Referencing a list within an expression will always result in iteration and is always substituted by a single item of the list.

Once the lists are set up, an assert is specified with an expression that uses both A and B. This makes the query engine iterate in nested loops for items of A and B, which means the expression is evaluated to every possible pair of items in list A and list B.

Or in other words, any possible pair of padstacks on the board - including the case of a virtual pair of the same padstack twice (since every padstack is present on both lists). For example if the board has three padstacks p1, p2 and p3, then A is [p1, p2, p3] and B is [p1, p2, p3] too. The first iteration will take the first item of A and the first item of B, so the pairing is p1 vs. p1. Avoiding such false checks is easy by starting the assert expression by (A != B), which will make the whole expression false for the virtual pair case.

Note: query has lazy evaluation so when the left side of an && if false, the right side is not evaluated. This it makes execution more efficient to add such simple checks that limit the number of objects or combinations to deal with as early as possible in the expression.

With that, we would be checking the same pairs twice still: p1 vs. p2 and then p2 vs. p1. The easiest way to avoid that is to consider that each object has an unique integer ID that can be compared, which allows us to write (A.ID > B.ID) instead of (A != B). This ensures only 'ordered pairs' are considered, at about the same low cost of check.

The left side of the && filters duplicates and self-overlaps. The right side is the actual check: it calculates the distance of the hole centers using the distance() builtin function and compares the result to the expected minimum distance which is the sum of the radii of the two holes. Since a hole is always at the origin of the padstack, the hole's center coordinate matches the padstack's coordinate. When A.x is written in the expression, that really means: "take the object from list A for the current iteration and extract its x property", which for a padstack means the x coordinate of the padstack placement.

When both the left side and the right side of the && evaluates to true, that means the pair is valid and unique and the holes overlap. At this point drc_query could already indicate the DRC violation, but it would use the last evaluated object for indicating the location. Which means the indication would mark only one of the two padstacks participating in the overlap.

The thus keyword changes this: when the left side of thus evaluates to true, instead of returning true, the right side is evaluated and returned. The right side is a function call to the builtin function violation, which will create a new list of instruction-objects pairs, inserting the current iteration object from list A as group-1 object and list B as group-2 object. In other words, it's a list of the two padstack objects participating in the overlap, sorted into two groups (red and blue on screen).

Call arguments for violation() are always pairs of a DRC* constant and an object or numeric value. There can be 1 or more pairs. The following DRC* constants can be used:

name meaning
DRCGRP1 following object is in group-1 of offending objects
DRCGRP2 following object is in group-2 of offending objects
DRCEXPECT following numeric constant is DRC-expected value (e.g. minimum or maximum)
DRCMEASURE following numeric constant is the measured value (that contradicts the expected value)
DRCTEXT following string or value is appended to the violation description as plain text

Using any of the above instructions is optional, with the following considerations: