pcb-rnd knowledge pool
History: how the old layer model was broken
old_layer by Tibor 'Igor2' Palinkas on 2016-11-13
Tags: insight, history, historic, layer, rewrite, data, model
Abstract: What problems we had with the old layer model inherited from PCB and how we got rid of these problems by the layer rewrite.
Before 2017, pcb-rnd had a real bad layer support code (inherited from gEDA/PCB). Copper layers were explicit: you could create, name and group them. But anything beyond that was an ugly hack:
- the outline layer was really a copper layer and the code decided it's for the router by looking at the name(!) of the layer
- there were always exactly two silk layers, they were always indexed as +1 and +2 after the last copper layer; this got ugly when you change the number of copper layers; it is also impossible to have multiple silk layers on the same side (different colors) or remove silk layer (e.g. single sided board; the workaround is to delete some gerber files manually...)
- the solder mask layer didn't really exist at all; the mask was calculated on the fly; many output plugins were aware of the solder "layer", but it's always a hack
- paste layer is even worse, it didn't exist either, but only a few output plugins even cared about paste
- any extra, user defined layer was a hack: comment layers, layers for metadata (e.g. automated testing) were all copper layers for pcb-rnd - there was no way to tell the code it was not copper; this interfered when you accidentally connect two vias with a comment...
Late 2016 I started a long term cleanup of this situation, because maintaining these hardwired hacks in the code is a nightmare and there's some user pull on editable solder mask layer. By now the layer rewrite is complete, with only a few minor assumption left in the code, that will be removed later (e.g. the "both sides have silk" assumption).
generated layers vs. explicit layers
If you generate layers and you need something done differently, you need to step back and change things indirectly. This often feels like a workaround. For example the paste layer was generated from the pad objects. The only control the user had was an ugly hack, the nopaste flag on pad, to not generate paste for a specific pad. This did not allow any other deviation, like smaller or larger paste or diffrent shaped (e.g. round) paste.
If you have explicit layers, you often need to draw (or generate) e.g. when creating a new footprint. Then if you want to make a global change on how these things are generated, it's harder if you already have a snapshot.
I don't think either of the above two would be a clear winner. I am considering a setup where you simply have multiple solder mask layers:
- one that is generated from the pads automatically - but this still would be an explicilty layer
- one or two where you can manually draw openings or fillings (draw and erase)
To produce a final output these layers are combined (composited). The same happens with silk and paste.
This method keeps the advantage of auto-generated pad masks/paste but would allow the user to randomly override it without any indirection. Like you figure you want your power line thicker with some extra solder, you could just draw a few lines over it on the "remove mask" layer. Or you do your first production run and turns out only one of the 6 qfn()s need more paste because of some via sucks away too much: you could go there and draw a drop of paste on the "add paste" layer, without having to change the footprint.
As of late 2017, we decided to remove pads in favor of padstacks, so the auto generated masks will be gone too. However, with padstacks, it's easy to modify the prototype so that all padstacks sharing the same prototype are updated. It should be also possible to make a change to one padstack reference by creating a new prototype so only that one padstack is affected.
pcb != lihata
We have support for distinct file formats, they don't mix. You either save in the gEDA/pcb format, and it will have 0 lihata in it, or you wave in the lihata board format.
The attribute-for-every-object feature is available only in the lihata board format. There's no plan to extend the gEDA/pcb format in any way - we will track reasonable mainline changes, but we won't change that format from pcb-rnd.
old system: indices
I'm in the middle of an almost total rewrite of the layer support. The goal is to remove various assumptions that the original code had hardwired repeatedly in most exporters and core. These assumptions limit what we can do with our layers without breaking everything immediately (see later). They also make the code unreadable and hard to maintain. Like if you know you have 4 copper layers, indexed 0..3, why do you address layer 5?
(Because the code assumes there will be two extra silk layers right after the copper layers. Which means you can't have more or less than 2 silks. Then one would think the same happens to top/bottom mask or paste layers, following the silks, but no, those are handled totally differently.)
These assumptions sort layers in two classes: the "copper" layers (including silks and outline layers!) and everything else (mask, paste, fab, drill, "the other side" layers). Instead of taking a layer as a layer, the code tries to do random strange things depending on whether it thinks the given layer is "copper" or not. Determining this sometimes need the layer index, sometimes the user editable layer name (!), sometimes the magic bits in the layer group index. Then if someone wanted to loop over all layers, it had to be done differently for "copper" and "non-copper" layers. Layer groups behaved differently per class too. The physical location of the layers, especially silk, mask and paste are not really stored, just assumed.
It's a mess. One can feel all the historical layers of code built up slowly in the spirit of "let's hack this in without having to clean this up" over the decades. By now we really reached a point where we have to bite the bullet and clean it up before we can proceed.
2016 year-end state and plans
The first step was to provide a new, simpler, more uniform and more flexible layer API in core. This API present all layers as equal, generic canvases, with a set of type bits that tells where the layer is and what's on the layer (e.g. "copper" on "top side", "silk" on "bottom side", "outline" (no side)). This also cleared up the terminology about layers, layer groups and their relation to physical layers (remember how much criticism PCB gets for this on geda-user@). This is all done by now.
The second step was rewriting the calls to use the new API. At the end of this step we had all the code doing exactly the same as before, but without random assumptions. All layer-related knowledge groupped in a single spot in core.
The third step that we could do after all those above, is the exciting one: we could extend the layer support without breaking anything. This means:
- real physical layers: thickness, material, explicit substrate layers (sort of required for FEM, but making these explicit will help with anything more complex than 2 sided hobby boards) - this is a low hanging fruit
- support for extra custom layers (comment layer, adhesive, multiple silks e.g. for multiple colors) - another low hanging fruit
- we will be able to think about explicit, user editable mask and paste layers (or even editable fab or assy layers!)
- easier implemention of blind&buried vias