PADS

PADS Layout, from Siemens, formerly Mentor Graphics, exports a board as an ASCII file with the extension .asc. px.FromKiCad converts it with kicad-cli, the command line tool of KiCad 10 or later, and returns the same dict of layers as for a KiCad board. See KiCad, Altium, Eagle and more for how the tool is found.

[1]:
import collections
import planex as px

The board

The example is the CLAYSIGHT MCU board from The Ohio State University, a two-layer carrier board for a Raspberry Pi Pico W. The file comes from the test data of KiCad’s PADS importer and is licensed under the MIT license, Copyright (c) 2024 John Simonis, John LaRocco and Qudsia Tahmina. Download: ClaySight_MK1.asc and its license.

[3]:
board = px.FromKiCad("ClaySight_MK1.asc")
for name, layer in board.items():
    print(f"{name:10} {len(layer.faces):4} faces, {1e6 * layer.mass:9.2f} mm²")
F.Cu        117 faces,   1071.10 mm²
B.Cu         92 faces,   1234.86 mm²
Edge.Cuts     1 faces,  10480.68 mm²
F.Paste      44 faces,     97.22 mm²
F.SilkS      74 faces,    437.64 mm²
F.Mask       44 faces,     97.22 mm²
B.Mask       44 faces,     97.22 mm²
PlanexWarning: FromKiCad: 25 texts left out (not supported yet)

The layers are those of a board drawn in KiCad. The two copper layers have their faces named by net and the drill holes cut out, the outline is Edge.Cuts, and the silkscreen, solder mask and paste layers hold what KiCad prints there. The text on the board is not read yet, and FromKiCad reports how much it left out.

Copper per net

The two copper layers are drawn in copper, with the board outline dashed (Figure 1). Each face carries the name of its net, so the copper can be grouped by net:

[4]:
fcu, bcu, outline = board["F.Cu"], board["B.Cu"], board["Edge.Cuts"]
for name, layer in (("F.Cu", fcu), ("B.Cu", bcu)):
    area = collections.Counter()
    for f in layer.faces:
        area[f.name or "(no net)"] += 1e6 * f.mass
    print(name, len(area), "nets, the largest:", [(net, round(a, 1)) for net, a in area.most_common(3)])
F.Cu 33 nets, the largest: [('VCC', 218.9), ('N$15664', 218.0), ('U1_6', 107.3)]
B.Cu 33 nets, the largest: [('VCC', 262.9), ('N$15664', 161.2), ('U1_26', 98.6)]
F.Cu, the top layer
(a) F.Cu, the top layer
B.Cu, the bottom layer
(b) B.Cu, the bottom layer
Figure 1: The copper layers of a PADS board, read through KiCad

The solder mask openings F.Mask and the silkscreen F.SilkS laid over the outline show the board as it comes from the manufacturer (Figure 2).

F.Mask and F.SilkS over the outline
Figure 2: The top of the board

A check against the Pico’s pin grid

The Pico W’s datasheet puts its 40 pins on a 2.54 mm grid with 1 mm holes, in two rows along its long edges. On the board, these are the 40 plated holes of 1 mm, which are cut out of the outline. Their centers give the rows and the pitch:

[7]:
pins = []
for hole in outline.faces[0].inner:
    (x0, y0), (x1, y1) = hole.bounding_box
    if abs(x1 - x0 - 1e-3) < 1e-7:
        pins.append(((x0 + x1) / 2, (y0 + y1) / 2))
x = sorted(p[0] for p in pins)
y = sorted({round(p[1], 9) for p in pins})
pitch = [1e3 * (b - a) for a, b in zip(y, y[1:])]
print(len(pins), "holes of 1 mm, rows", round(1e3 * (x[-1] - x[0]), 6), "mm apart, 7 × 2.54 =", 7 * 2.54)
print(len(y), "pins per row, the pitch differs from 2.54 mm by up to", round(1e3 * max(abs(s - 2.54) for s in pitch), 4),
      "µm")
40 holes of 1 mm, rows 17.78 mm apart, 7 × 2.54 = 17.78
20 pins per row, the pitch differs from 2.54 mm by up to 0.254 µm

The rows are seven pitches apart, and the pitch is 2.54 mm to within 0.254 µm, a hundredth of a mil. That difference is in the PADS file itself, which stores some of the pins that far off the grid, and the conversion keeps it.