pcb-rnd knowledge pool
Layer group subtyping: the purpose field
layer_purp by Tibor 'Igor2' Palinkas on 2018-09-11
Tags: insight, layer, group, layergroup, purpose, binding, outline, boundary, mech, route, routing, plated, unplated
Abstract: How the layer system got extended with a layer group purpose, that behaves like a free form subtype. Reasons to do this instead of introducing hardwired bits for the new types. How this system works with existing mechanisms such as layer binding.
History: from PCB to pcb-rnd
In the original PCB code, there was no layer type indication of any sort. There were some heuristics and assumptions instead: if you had N layers then the first N-2 layers were copper or outline layers and the last two layers were silk layers (in a specific order to know which one is top or bottom). The outline layer was really just a copper layer with the name "outline" - and the code at some points checked the name of non-silk layers to figure if it was outline.
There were layer groups, used only for two things: tell if a layer is top or bottom and control which layers' visibility are toggled together.
This changed in pcb-rnd starting from late 2016, during the big layer rewrite, which was the first step of the data model rewrite. In the new model, layer groups have a so called layer type bitfield. There are some bits to specify the type of the layer group (e.g. copper or silk or mask), and another few bits to specify the location (top, intern, bottom, virtual).
This new model also makes a clear separation between "physical layers" (layer groups) and "drawing canvases" (layers). Layers are used only to make different colors or compose a group by drawing positively (adding ink) or negatively (erasing ink).
Purpose field: user defined flexibility
The above system is flexible enough that we could introduce editable solder mask and paste layers. However, there are two major areas it could not cover:
- when there are too many groups that are very similar in their main type, but differ slightly in some aspects; for example an "outline" layer group used really for board outline and another "outline" group used for plated slots. They are both done using a router, so the physical meaning is basically the same, but they need to end up on different output files in some exports and even the code may need to handle them differently (e.g. a plated slot conducts, but the unplated board outline does not)
- bits assume there is a finite set of layer group types, all understood by the code - there is no room for user defined types and especially no room for extra types which are understood only by optional plugins.
Because of the second, the trivial upgrade was adding a second, "subtype" field, called the purpose of the layer group. This is a free form text, so that the user can specify any value. This allows us to write plugins that look for specific layer group types, not depending on group names but flexible, extensible subtypes.
But once this system was in place, I realized that it can solve the problem with the "too many routed types" as well. The outline layer type got removed and three new types got introduced.
- boundary is anything that determines or modifies the boundaries of the board (I wanted to pick a different name than outline to avoid confusion with the previous setup)
- mech is mechanical layer in general; anything that will be manufactured by the fab. This includes non-boundary (internal) routed slots, cutouts but can indicate adhesives, glue, special marking, etc.
- doc is a generic documentation type; it is normally not sent to the fab, and it is not used to manufacture anything. Doc layers can be aide to the designer (e.g. enclosure shape, indication of high voltage insulation gap or groups of components). But some plugins will also use doc layers as input, for example the new DRC plugin can handle keepout info stored in doc layers (using scripts).
For the user this means that for some layer group types it is not enough to specify the main type (e.g. boundary) but the purpose (subtype) needs to be set too, to refine the meaning (e.g. uroute or proute). Or, in other words, when the user is browsing layer types from a list, the items are not a single type, but a type-purpose pair - which is not more complicated and won't make the list longer either. In return, the same mechanism can handle flexible/custom user subtypes and can be extended in the future without rising compatibility questions.
Purpose vs. location
There are two kind of layer groups: ones that are participating in the stackup and others that don't.
The location bits of the stackup layers are set automatically as they are deduced directly from the stackup. For example the topmost copper layer group is marked with the TOP location bit. The direction of deduction is always stackup -> bits, so changing the location bits in the layer type bitfield wouldn't change the physical location of the group, only would make the group data incoherent. There are also some restrictions, for example there can be only one top copper group, only one top silk group, etc. An atypical example is all the boundary and routed/cut/punched mech layers: they are part of the stackup and are forced to be global (cutting through all layers). Non-cut mech layers, such as adhesives belong to the off-stackup group (see below).
Other layer groups are off-stackup, which means they do not exist as part of the board stackup but as independent data - however, they are often marked with location bits to know which board side they refer to. For these layers the primary source of information is the location bit in the main layer type bitfield, as set by the user. Typical examples:
- mech layer for adhesives: can be top or bottom
- documentation layers, e.g. various keepout layers
Layer binding vs. purpose
Layer binding is semi-automatic: a subcircuit or a buffer has a layer recipe and pcb-rnd tries to find the best match for that recipe on the current board. This is done in two steps:
- 1. the layer recipe's type (which includes location and main type) must match the target layer's type (which is its parent group's type)
- 2. then other fields like name, compositing flag and purpose are compared; if they match, scores are awarded, and at the end pcb-rnd picks the best match (highest score) if there were multiple matching board layers.
In practice this means if the board has two boundary layers, one with purpose set to uroute and the other set to proute and there's a recipe that wants a boundary layer but does not specify purpose, pcb-rnd will pick either layer (semi-randomly). To make sure the layer recipe addresses the right layer, it should contain a purpose field that is either uroute or proute.
However, if then the recipe now with the purpose field set to proute is applied on a board where there's only a "classic outline" layer, that is a boundary layer with purpose uroute, it will be bound to that layer automatically. That is because it's the closest thing we have, with the highest score.
On one hand this is flexibility: on a simpler board/fab combination the user decides to keep only 2 routed layers, one plated, one unplated, instead of keeping 4 (separate set of plated/unplated for "outer" boundary and for "inner slot" mech). Because of this flexible mechanism, layers intended for slotting will silently end up on the next best routed layers, the boundary layers. No user interaction needed. But when the fab requires separate slot layers to be sent, the user would create all 4 layer groups and the same bindings would automatically prefer the mech layers for slots if the recipes were set up correctly.
On the other hand this is a risk too, which we may want DRC to capture long term: a padstack slot can easily become unplated because there's no plated routed boundary/mech layer available. Note: the choice to make this not an error, and rely on later DRC checks is thought over and intentional.