pcb-rnd knowledge pool


Zone based short checking

drc_zone_clr by Tibor 'Igor2' Palinkas on 2020-05-18

Tags: howto, drc, drc_query, query, zone, short, net, network, clearance, gap, drc_script

node source



Abstract: On some complext boards a single global value for copper-copper gap between different networks is not sufficient, but the board has "zones": different regions where different rules apply. A typical example is when the power supply section of the board has larger gaps for high voltage reasons while the rest of the board should be routed tightly with low voltage signals. This article describes how to implement the DRC checks differently per region: with custom doc layers, polygons and a simple drc_query script.


Major components of the solution

The solution presented here does not depend on any special "zone" feature but is implemented using existing, generic tools pcb-rnd offers. These tools and features are:

Theory of operation

The concept is that the user draws regions as polygons on a custom doc layer. The doc layer's group has purpose=zone to make sure the drc script will not pick up "zone polygons" from other layers groups. Since it is a doc layer, it typically won't end up in the export (e.g. in gerbers).

Then there is a custom drc_query rule, a short script, that lists all such "zone polygons" and does an extra check on copper object-object distances starting from any object that overlaps with any of the "zone polygons", in case the two objects are on different nets.

How much gap is required between the two objects is a value retrieved from an attribute of the "zone polygon". The attribute is called min_copper_clearance and expects a coordinate value (a number with unit, e.g. 15 mil or 2 mm).

That means each "zone polygon" defines its own numeric value for the minimal clearance so the board can have as many different region with different DRC clearance values as needed. The rule does not check gaps on objects not overlapping with any "zone polygon" - that is, on the regions outside of the "zone polygons".

This working example demonstrates how this works. There are two networks on the netlist, the upper trace and the lower trace. There are two regions when they get close to each other. The minimum distance between the two pairs of horizontal line segments are the same on the left and right side. There are two "zone polygons" drawn, with the min_copper_clearance attribute set differently, so that the right side polygon's value is greater than the gap between the horizontal line segments. Thus a DRC violation is generated for the right side.

Tip: turn off the visibility of the zone-clr layer to see the traces.

Script details

The drc_query script to use is:

      ha:zone_clr {
       type = shorted nets
       title = net too close to other net (zone)
       desc = insufficient clearance between an object of the network and objects of other networks
       query = {
rule zone_clr
let Z ((@.type == POLYGON) && (@.layer.purpose == "zone") && (@.a.min_copper_clearance)) thus @
let A  (@.type == PSTK) || (@.layer.type == COPPER)
let B A
assert (A.IID < B.IID) && (overlap(A, Z) || overlap(B, Z)) && (A.netname != B.netname) && intersect(A, B, Z.a.min_copper_clearance) thus violation(DRCGRP1, A, DRCGRP2, B, DRCEXPECT, Z.a.min_copper_clearance)

The drc_query script can be loaded the usual ways .

The type, title and desc fields are administrative for the violation report. The actual script is in the query string.

The rule keyword announces a multi-line script. Then we first collect all "zone polygons" in list Z. A "zone polygon" is an object of type POLYGON sitting on a layer whose layer group's purpose is "zone". Furthermore we require that the "zone polygon" has a non-empty @.a.min_copper_clearance field (because that is the numeric value we plan to use for the check).

Then we collect all padstacks and copper layer objects in list A and make a copy of this list in B. This will help us iterate on pairs of A-B objects.

The assert line is the one that performs the actual job. First it requires that A.IID is less than B.IID - so that we deal with A-B but not with B-A, so we won't report the same violation twice. Then we check if either A or B overlaps with the current "zone polygon" we are investigating from list Z. If so, we require that the netname of A and B differ, because if they are on the same net, no gap is required.

When we got here, we have two objects, A and B, from which at least one is under the "zone polygon" Z, and we know we need to have a gap between them because they are on different nets. So the next thing to do is checking the gap, by checking if A and B intersect if one of them is bloated up by Z.a.min_copper_clearance.

If all these sub-expressions are true, the whole expression is true and the right side of the thus operator runs, which returns (creates) the DRC violation, listing the two objects as the red and blue groups and prints the expected value of the gap as read out from Z.a.min_copper_clearance.

Note: overlap() and intersect() are almost the same functions, with the same parameter conventions. The only difference is that overlap() ignores layer differences while intersect does not. For the "is this object under the zone polygon", we have to use overlap() because the zone polygon will be on a different layer group. For the actual "is the gap large enough between the copper features" question we need to use intersect() because objects being too close on the plane, but being on different layer groups means no short possible.

The global (non-zone) rule

The board will probably have regions not covered by any zone and the natural thing to do is to apply the original, global, no-zone rule for those regions. However, since that rule has no idea about zones, it will really run everywhere.

Alternatively the global rule could be disabled, but that would leave non-zone regions unchecked. It is not trivial to write a specific rule for the non-zone regions, because it would require the script to say "the object is not overlapping with any of the zone polygons" which is not easy to formulate in the query language.

However this all can be avoided by choosing the min_copper_clearance values carefully: the smallest value must always be assigned to the global min_copper_clearance , which will make this the non-zone value. Then zones are anything where greater gap is required.

In other words, the default, non-zone gap assumption must be the smaller gap used on the board, and any zone is defined for regions where larger gaps are needed for some reason.

(This is not terribly efficient, tho: both the global and the zoned rule will run for every object covered by zones. Performance can be improved by disabling the global rule and making sure the board is fully covered by non-overlapping zone polygons.)

Tips & tricks

Auto-hiding the zones

The zone polygons are large and cover interesting board graphics. They should be visible only a few times, when they are set up. Turning off the visibility of the zone layer manually after every time the board is loaded may be annoying. This can be avoided setting the init-invis attribute of the layer group to 1.

"Zone poly" holes

Polygon holes can be explicitly drawn into the "zone polygons". This can help a lot in the typical situation when the whole board needs to have e.g. 0.3 mm gap, but a small region in the middle is allowed to have 0.2 mm gaps. In this case the global rule should be 0.2 mm while the zoned rule 0.3mm. The zone should be one large polygon covering the whole board, with a cutout for the middle region where the 0.2mm should apply.


It is possible to draw polygons that clear other polygons . If there are multiple, non-rectangular zones that should "share" a contour without overlap, using this feature with a small clearance value can make editing the "zone polygons" much easier.

multi-layer options

With a slight modification of the script, the min_copper_clearance value for the zone could come from the "zone layer", not from the "zone polygon". Then multiple "zone layers" with different gap values and layer color could be created within the same layer group.

This setup would visualize the different zones by different color at a cost of having more layers. (Note: in the layer selector widget, the group can be collapsed by clicking its group name.)

This setup may also have advantages in case the design has a lot of small, disconnected zones that should have the same gap value and the gap value may change in the future; in this setup there's only one place where the value needs to be modified, and that's the layer's attribute.

However, this setup is just overhead for the simplest cases, where there are only 1 or 2 zones.