Manually panelizing complex PCBs in KiCAD for use with JLCPCB
As you probably know, a PCB panel is a single printed circuit board which contains multiple distinct boards, all of which remain mechanically linked for further manufacturing. For example, this is a simple 2×2 PCB panel with boards separated by V-cuts:
A simple panel generated by KiKit.
A V-cut is a V-shaped groove running on opposing sides of the panel (front and back). It leaves the panel structurally sound but weakened, making it easy to break out the individual boards. There are other alternatives such as “mouse bites”; you can read more about them in JLCPCB’s tutorial.
The individual boards on the panel are often identical (as in the example above), but can also be completely different boards. Of course all the boards on the panel must have the same stackup (number and type of layers, dielectric type, etc.), because they ultimately are the same PCB, just broken up.
Motivation
Panels simplify manufacturing of both the PCBs themselves and the devices that are built from them. The reason is the same for both: there is a fixed cost associated with a unit of work, so making sure the units are large lowers the cost per unit. In other words, when you load a board into a machine, it doesn’t really matter how large the board is (within practical limits, of course), the cost is roughly the same.
In fact, volume PCB manufacturing is always done in panels, where multiple customers’ boards are combined into a large panel and processed together. Of course, eventually you need to break out the boards from the panel, but at any kind of scale, that trade-off is well worth it.
Panels are great for electronics assembly, too. Feeding a single panel through pick-and-place and reflow is significantly easier than handling the individual boards. In fact, we aim to keep our devices panelized for as long as possible, and even program, test, and calibrate them as a unit. Also, a panel of 16 boards is mentally much easier to handle for humans than 16 discrete boards.
We’re not (yet!) manufacturing in volume, but with IFA around the corner and several open requests for samples, we needed to urgently make a fresh batch of our most recent designs.
Our Airgeek A1 is full of fine pitch BGAs and wafer-level packages, so a high-precision 6 layer board is a must. These aren’t your “5 pcs for $2” boards that made JLCPCB so popular among hobbyists, and every design revision thus comes with non-negligible cost. Panelization is a great way to keep the cost down, especially when you need several distinct designs at the same time, and you only need a few units of each design.
Airgeek A1 revision 4a requires:
-
The motherboard.
-
A daughterboard for the NO2 sensor.
-
A daughterboard for the CO sensor.
-
Additionally, every major revision of the motherboard requires a new test rig to interface with.
-
Since the M1 is developed and tested alongside the A1, we need several M1 boards for testing, too.
That’s five boards and a cost probably in the high hundreds of dollars, if not thousands. This time, we needed to rush the order—further increasing the cost—and I finally got the excuse I needed to learn how to panelize PCBs.
Panelization in KiCAD
KiCAD doesn’t natively support panelization. Most of the KiCAD panelization tutorials online either use external tools (such as EasyEDA), or the amazing KiKit utility written by Honza Mrázek.
Panelizing regular boards
If all you need is to panelize a regular (square, rectangular or circular) board, I would highly recommend just looking at the KiKit examples. With a regular board, using KiKit is a breeze.
That’s it.
Panelizing irregular boards
Airgeek A1 revision 4a motherboard. Now that I think of it, I forgot to put “Rev 4a” onto the board…
Unfortunately our boards are irregular. Revision 4a contains temporary tabs for alignment within our reworked 3D-printed core. We plan to ditch them for production, but the lens assembly for BMV080 will remain.
Panelization of irregular boards seems to be a much more manual process. At least I didn’t find a way to do this conveniently in KiKit, and when you think about it, finding a layout that is space efficient is difficult in its own right.
In the end, the following process worked quite well for me:
-
Before you begin, make sure the boards are in perfect shape (literally) and that they pass Design Rule Check (DRC). Fixing any issues after the boards have been panelized is expensive.
-
Write a Python script which invokes KiKit to combine the individual boards into a single PCB design (panel.kicad_pcb). This gives you a single PCB design with the discrete boards.
-
Create the panel layout from the Edge.Cuts layer in another stand-alone instance of pcbnew (panel_layout.kicad_pcb). This is the most time-consuming part, so it makes sense to make it as reusable as possible. If you need to fix your boards later, you won’t have to redo this step, unless you also change the board outline.
-
Copy the panel layout design into your panel design and lock it. Move the boards to their correct locations in the layout and then delete their Edge.Cuts layer.
-
If you need to make any adjustments, you can either repeat the previous step by hand, or you can automate it with another invocation of KiKit.
Here’s the individual steps in more detail.
Step #1: Getting the boards ready
Do the usual drill for each member board: sync with the schematic, run Cleanup tracks and vias, unify text formatting, do any minor last minute changes1, pass DRC, triple check everything and you’re good to go.
Also, if you’re panelizing as an afterthought, make sure that the board stackup, constraints and DRC violation severity are the same for all the boards. This is best done by choosing the most complex board (in our case, that’s the motherboard) and using Load settings from another board in the Board Setup dialog to configure the other boards identically.
Step #2: Combining the boards with KiKit
I wrote the following trivial piece of Python:
#!/usr/bin/env python3
from pcbnewTransition.pcbnew import LoadBoard, VECTOR2I, BOX2I
from kikit.panelize import Panel
from kikit.units import mm
a1r4_filename = "a1r4/a1r4.kicad_pcb"
m1r4_filename = "m1r4/m1r4.kicad_pcb"
a1r4_m1r4_tester_filename = "a1r4-m1r4-tester/a1r4-m1r4-tester.kicad_pcb"
co_filename = "spec-co-r1/spec-co-r1.kicad_pcb"
no2_filename = "spec-no2-r1/spec-no2-r1.kicad_pcb"
# Load the motherboard.
# This loads the *.kicad_pcb file.
a1r4 = LoadBoard(a1r4_filename)
# Create a new Panel object which inherits settings from the motherboard.
# The panel will use the motherboard's stackup.
panel = Panel("panel.kicad_pcb")
panel.inheritDesignSettings(a1r4)
panel.inheritProperties(a1r4)
panel.inheritTitleBlock(a1r4)
panel.inheritLayerNames(a1r4)
# For some reason, board bounding box detection didn't work for me.
# I just set the board area to 1x1 meter.
area = BOX2I(VECTOR2I(0*mm, 0*mm), VECTOR2I(1000*mm, 1000*mm))
# Append all the boards to the panel.
# Locations can be anything as long as boards don't overlap.
#
# Some boards may be on the panel multiple times.
# I highly recommend that you explicitly add them multiple times.
# I do this below for the CO/NO2 sensor daughterboards.
panel.appendBoard(a1r4_filename, VECTOR2I(0 * mm, 0 * mm), area)
panel.appendBoard(m1r4_filename, VECTOR2I(100 * mm, 0 * mm), area)
panel.appendBoard(a1r4_m1r4_tester_filename, VECTOR2I(0 * mm, 100 * mm), area)
panel.appendBoard(co_filename, VECTOR2I(0 * mm, 250 * mm), area)
panel.appendBoard(co_filename, VECTOR2I(0 * mm, 290 * mm), area)
panel.appendBoard(no2_filename, VECTOR2I(30 * mm, 250 * mm), area)
panel.appendBoard(no2_filename, VECTOR2I(30 * mm, 290 * mm), area)
# Write the panel PCB to panel.kicad_pcb.
#
# Without reconstructArcs, arcs will be approximated by line segments.
# This makes a mess of the follow-up steps.
panel.save(reconstructArcs=True)
You’ll probably get the following error at the end:
RuntimeError: Merging of DRC rules of multiple boards is currently unsupported
Providing that all your boards have the same DRC settings, I believe you can safely disregard this error. The panel PCB gets created just fine (yes, it took me a while to notice).
Important note: Don’t use File → Append board… in pcbnew, it
does not rename nets. If you append multiple boards which share a net this
way, you will get a countless DRC violations for the missing connection between
all pairs of corresponding nets. This creates so much noise that it’s virtually
impossible to DRC the panel before manufacturing, removing a vital check at the
end of the panelization process. KiKit internallly renames the nets to
Board_{n}-{net}
, avoiding this problem completely. That’s also why you should
call panel.appendBoard
multiple times for the same source board rather than
duplicate the board in pcbnew.
The output of KiKit in my case is the following union of the five boards, with
two instances of NO2 and CO sensor daughterboard each. As you can
see, I placed the boards liberally by eyeballing the right arguments to
appendBoard
.
Output form the Python script using KiKit. The boards are placed somewhat randomly and it doesn’t matter. Yes, it bothers me too!
Step #3: Creating a layout for the panel
This is the tricky part. I’ve spent several hours trying to build the panel directly by connecting the member boards in various ways, but to no avail. In the end I’ve found a much simpler way.
What didn’t work
To illustrate my original approach which didn’t work, let’s focus on the four small CO/NO2 daughterboards. I would group the boards one by one, so that they are easier to work with as a unit2, align them vertically and distribute horizontally using the Align/Distribute context menu.
The four boards (four groups) aligned vertically and distributed horizontally. The idea is to run a join them into a panel now.
The idea was to add throwaway strips of PCB to the top and bottom that will hold the boards together in a panel, with V-cuts for later breakout. The other boards would follow, making a nice large panel.
One thing I didn’t mention yet is how to communicate where the V-cuts are supposed to be to JLCPCB engineering. It’s quite simple: when you have two boards and their outlines are touching one another, i.e. you place the boards back-to-back—that is a V-cut. If you’re familiar with KiCAD, you probably know that board outlines touching is a DRC violation, and the board won’t render properly in preview. For example, if we attempt to join the boards like this:
This approach, while very simple, suffers from several problems. The magenta box shows the area where the daughterboards are touching the PCB strip. This is a way to communicate V-cuts to JLCPCB, but also a DRC violation.
Unfortunately, we get a bunch of DRC violations where the board outlines intersect, and the board view is completely useless, too.
When the 3D viewer is unable to determine where is inside and where is outside, it gives up. Instead, the bounding box of the components becomes the board outline.
And there’s a much bigger problem: it’s not possible to mill these boards. Consider what happens when the milling bit (magenta circle) hits a tight corner. JLCPCB is using a milling bit with 0.5 mm radius for board outlines, so the smallest radius the tool can clear is roughly 0.5 mm.
The 0.5 mm cutting tool cannot possibly clear the tight corner without taking a piece of the PCB strip with it. The magenta circle represents the tool bit (to scale). The partially transparent magenta area is the material the tool must remove in order to cut the desired board outline.
JLCPCB engineering would probably refuse this on the grounds that it’s your job to indicate what’s supposed to be milled; they can’t make these choices for you. Though I’m not entirely sure. My experience with them is that the service is perfect as long as I don’t get any questions; otherwise it’s really easy to pick up significant delays in the back-and-forth with their engineers. So I tend to be very careful and leave nothing open to interpretation.
I tried fighting KiCAD and the 3D viewer by placing the strips a bit farther from the boards, with the intent to move them back to their correct location later on, but that’s just nuts—and it doesn’t address the milling issues.
What seems to work fairly well
After some additional struggle trying to fix the milling issues, I realized that I’m basically recreating the outlines of the boards, and got the idea of focusing on the negative space (what is a board is not) instead. I started another project in a separate instance of pcbnew and copied only the board outlines from the panel PCB:
A separate project with just the board outlines. We’ll use the outlines to create a layout for the panel.
Again, I created one group per outline to make them easier to manipulate. Then I laid out the outlines as I saw fit. The only thing to keep in mind is that the V-cuts must run across the entire board, perpendicular to the board edge (either along the width, or the length or both). Then I drew a rectangular outline around the boards which represents the panel. Align/Distribute is your friend.
Instead of trying to connect the boards together, I imagined cutting the board outlines out of a panel. The board outlines accomplish just that.
I was fairly generous with space. I believe a minimum of 5 mm is required for the board strips; I have opted for 8 mm to steer clear of trouble. The layout could be made much more efficient, and we could also make the panel up to 300×300 mm large for very little additional cost, but right now that’s beside the point.
I would now like to add the V-cuts, but I really want to keep using the 3D viewer, and if I place the V-cuts onto the Edge.Cuts layer, I’ll make the outline malformed again. So for now, let’s place them onto F.Silkscreen:
The yellow lines on F.Silkscreen are the future V-cuts on Edge.Cuts.
All that remains now is to carve out the non-straight parts of the boards. That’s simple to do, if a little tedious.
I start by adding a circle with a 3 millimeter radius around the first corner. The center of the circle is the same as the circle of the arc defining the rounded corner. The choice of radius is arbitrary, but it’s a good idea to create a tool path wider than the tool.
I then trace out a 90-degree arc concentric with the circle. This again is easy to do, just make sure you have the Edge.Cuts layer selected as active, so that tools snap to it; or you can enable “snap to all layers” with Shift–S. It also helps to constrain tool movement to the axes and the angles to multiples of 45 degress with Shift–Space.
We create these “beans” around the corner. Use Duplicate (Ctrl–D) to copy the shape around.
You can then easily create a tool path along the outline of the board. Because we used a tool path larger than the radius of the milling tool, the sharp turn in the inner corner is not a problem.
At the end of this step, you should have an empty panel with cut-outs resembling the member boards. Mine looks like this:
The V-cuts hold the boards together, but allows us to break out the straight parts of the boards out of the panels easily. The non-straight lines are milled. Note that I added tooling holes and fiducials as required.
A 3D view of the complete panel layout.
Save the panel layout as a separate PCB, you might need it if you need to re-do the panel in the future. And don’t forget to add tool holes and fiducials!
Step #4: Populate the panel with member boards
The rest is simple: just copy the layout back to the original panel, group it and lock it. Then move the boards into their respective openings. Disable Locked items in the selection filter to protect the layout, select all, ungroup all, deactive all layers except Edge.Cuts, select all and delete.
This sequence removes the Edge.Cuts of the original member boards (but not of the layout which is protected), leaving you with this:
The panel layout with the boards inside. We’re almost done.
A 3D view of the complete panel.
If everything is to your liking, move the V-cuts from F.Silkscreen to Edge.Cuts and you’re done!
Conclusion
Although this process is quite labor intensive, it’s very flexible and works even for very irregular boards. Once you get the knack for drawing the tool paths, it’s actually quite easy to get to the panel layout. If you need to update the boards, you can reuse the layout.
I think it should be fairly simple to use KiKit to populate the layout with up-to-date boards, providing you take the time to fiddle with placement. If you expect to update the boards often (without changing their outline), it could be a good idea.