Restack to four signal layers and route the board
The Ti375 fanout could not be resolved with three routing layers, so L5 stops being a dedicated 1.8-V plane and becomes a signal layer. No rail owns a whole layer any more: the supplies are split into zone islands on L4 and L6, L2 and L7 stay solid and completely unrouted, and F.Cu, L3, L5 and B.Cu are all available for signals. Routing is staged rather than handed to the autorouter wholesale. The BGA escape, the buck high-current loops, plane stitching and the local power completion all run first and are locked; Freerouting then sees only the ordinary digital classes, with everything else present as fixed copper. finalize_routing.py installs the audited RF and USB geometry and closes whatever the autorouter left. Four bugs behind the routing failures, all of them the same shape - a layer list written down instead of read from the board, which was correct while L5 was a plane and silently wrong afterwards: - export_routing_stage.py emitted via keepouts for three layers only, so every existing via was invisible to the router on L5. It drove traces straight through them: 102 shorting_items in DRC. - route_local_power.py never used L5 at all, and picked up escape vias on two layers instead of all four. - add_rf_ground() judged fence positions against vias and pads but not tracks. It runs after the session import, when that region already has routed copper, and dropped a ground via on top of a signal trace. route_local_power.py also grew a via cloud on every rail: reaching a plane counted as progress even when it merged nothing, so the same pad collected a via per pass, 0.4 mm further out each time. Progress is now measured by the group count actually falling, and groups already sitting on their plane are skipped. That removed 404 redundant vias. The MAX2771 PLL loop filter was spread across three corners of the package, which forced the charge-pump node onto an inner layer under the GPS front end. All three parts now sit in the pocket at U21's north-west corner and the net stays on F.Cu. A permanent RF_NO_DIGITAL area plus a scoped rule keeps that region free of non-power copper on L3/L5/L6/B.Cu, and the local router enforces the same restriction, since it is the one stage that runs after the temporary keepout is deleted. Also enables the solder-mask bridge check that was switched off board-wide; the board passes it with no violations. Final board: DRC clean - no shorts, clearance, disallowed-item or dangling-copper violations - with 26 unconnected endpoints left. Routing is spread 37% L3, 27% F.Cu, 26% L5, 10% B.Cu over 6548 mm and 971 vias. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+183
-60
@@ -29,6 +29,13 @@ STOCK = Path("/usr/share/kicad/footprints")
|
||||
# field allow a compact 155 x 90 mm outline.
|
||||
X0, Y0, X1, Y1 = 20.0, 20.0, 175.0, 110.0
|
||||
|
||||
# Anti-pad clearance for planes that have to stay continuous under the Ti375's
|
||||
# 0.8-mm escape via grid. This is the board's declared minimum copper
|
||||
# clearance; anything larger merges neighbouring anti-pads and erases the
|
||||
# plane across the whole BGA footprint. Split L4 islands keep the wider
|
||||
# default so the gaps between different DC rails stay generous.
|
||||
PLANE_CLEARANCE = 0.10
|
||||
|
||||
|
||||
def mm(value: float) -> int:
|
||||
return pcbnew.FromMM(value)
|
||||
@@ -91,9 +98,40 @@ SPECIAL = {
|
||||
# likewise land on the two controlled-impedance sections.
|
||||
"C83": (167.5, 42.1, 180), "C84": (162.15, 41.45, 90), "R39": (160.5, 42.1, 180),
|
||||
"C85": (159.0, 40.70, 90), "L4": (169.5, 42.585, 90),
|
||||
"L1": (105.0, 91.0, 0), "L2": (115.0, 91.0, 0), "L3": (130.0, 91.0, 0),
|
||||
"FB1": (60.0, 88.5, 0), "FB2": (36.0, 49.0, 0), "FB3": (39.0, 46.5, 0),
|
||||
"R63": (37.0, 55.50, 0), "R64": (37.0, 54.50, 0),
|
||||
# Converter magnetics and capacitors form compact current loops. U13's
|
||||
# SW bank faces south, so L1 is vertical; U14/U15 SW pins face east and
|
||||
# their inductors/output capacitors continue directly to the right.
|
||||
"L1": (100.0, 98.5, 270), "L2": (118.0, 97.0, 0), "L3": (133.0, 97.0, 0),
|
||||
"C18": (95.7, 91.8, 180), "C19": (95.7, 94.4, 180),
|
||||
"C20": (104.2, 91.8, 0), "C21": (104.2, 94.4, 0),
|
||||
"C22": (95.5, 103.5, 270), "C23": (99.0, 103.5, 270),
|
||||
"C24": (102.5, 103.5, 270), "C25": (106.0, 103.5, 270),
|
||||
"C26": (112.5, 96.2, 180), "C27": (112.5, 97.8, 180),
|
||||
"C28": (121.3, 96.0, 0), "C29": (121.3, 98.3, 0),
|
||||
"C30": (127.5, 96.2, 180), "C31": (127.5, 97.8, 180),
|
||||
"C32": (136.3, 96.0, 0), "C33": (136.3, 98.3, 0),
|
||||
# Core remote-sense and compensation stay on the quiet north side of U13.
|
||||
"C16": (96.2, 89.0, 180), "R29": (98.1, 89.0, 180),
|
||||
"C17": (100.0, 89.0, 0), "R27": (104.0, 87.8, 180),
|
||||
"R28": (104.0, 89.2, 180),
|
||||
"R31": (115.0, 94.3, 0), "R32": (130.0, 94.3, 0),
|
||||
# FB3 feeds the FT4232H PLL supply on pin 9, which sits south of the USB
|
||||
# pair corridor. Keeping the bead on that side avoids a supply trace that
|
||||
# would otherwise have to cross the reserved differential run.
|
||||
"FB1": (60.0, 88.5, 0), "FB2": (36.0, 49.0, 0), "FB3": (36.5, 58.2, 0),
|
||||
# The 12-MHz series damping resistor moves west with its oscillator to keep
|
||||
# the space south of the FT4232H free for FB3.
|
||||
"R67": (32.0, 58.0, 0),
|
||||
# USB series pair and FT4232 REF resistor follow the device pin order.
|
||||
"R62": (37.0, 55.50, 0), "R63": (37.0, 54.50, 0),
|
||||
# R64 is the FT4232H reference resistor, not part of the pair; it moves off
|
||||
# the terminator column so the reserved pair corridor stays empty.
|
||||
"R64": (36.0, 51.50, 0),
|
||||
# The shield network belongs at the receptacle shell, not east of the
|
||||
# contacts: automatic placement filled the only corridor that carries
|
||||
# USB_D_P/USB_D_N away from J1. Pin all three parts south of the shell,
|
||||
# clear of the pair's diagonal run towards the series terminators.
|
||||
"C2": (24.0, 55.5, 0), "R9": (26.5, 57.5, 0), "R10": (29.5, 57.5, 0),
|
||||
# MAX2771 output damping sits at the receiver; FPGA-driven 3-wire control
|
||||
# damping sits at the BGA source. R55 holds SHDN low at the receiver.
|
||||
"R45": (151.0, 44.5, 0),
|
||||
@@ -102,7 +140,16 @@ SPECIAL = {
|
||||
"R50": (160.5, 47.5, 90),
|
||||
"R51": (121.5, 65.5, 0), "R52": (121.5, 66.7, 0),
|
||||
"R53": (121.5, 67.9, 0), "R54": (121.5, 69.1, 0),
|
||||
"R55": (161.7, 44.5, 90), "C89": (146.5, 53.0, 0),
|
||||
"R55": (161.7, 44.5, 90),
|
||||
# PLL loop filter. CPOUT is a high-impedance charge-pump node, so the
|
||||
# whole branch sits in the free pocket at U21's north-west corner rather
|
||||
# than being spread around the package: pin 1 is 2 mm from the 15-pF shunt
|
||||
# and the entire net stays on F.Cu. Scattering these parts is what forced
|
||||
# CPOUT onto an inner layer underneath the GPS front end. The column stops
|
||||
# short of x = 154.4 so it never enters the reserved RF corridor.
|
||||
"C88": (153.60, 40.80, 90),
|
||||
"R43": (153.60, 38.85, 90),
|
||||
"C89": (154.80, 38.85, 90),
|
||||
# 40-MHz termination is at U24. SafeSPI controller outputs are terminated
|
||||
# beside the FPGA; MISO and EXTRESN bias remain beside the IMU.
|
||||
"R56": (140.2, 54.35, 0),
|
||||
@@ -283,11 +330,17 @@ def add_stackup(board: pcbnew.BOARD) -> None:
|
||||
board.SetLayerName(pcbnew.In1_Cu, "L2_GND")
|
||||
board.SetLayerName(pcbnew.In2_Cu, "L3_SIG")
|
||||
board.SetLayerName(pcbnew.In3_Cu, "L4_PWR")
|
||||
# L5 is a routing layer, not a rail. Declaring it signal is what puts
|
||||
# it into the Specctra export as routable: a layer left as "power" is
|
||||
# offered to the router as "not used for routing", which is exactly the
|
||||
# congestion this stackup exists to avoid.
|
||||
board.SetLayerName(pcbnew.In4_Cu, "L5_SIG")
|
||||
board.SetLayerName(pcbnew.In5_Cu, "L6_SIG")
|
||||
board.SetLayerName(pcbnew.In5_Cu, "L6_PWR")
|
||||
board.SetLayerName(pcbnew.In6_Cu, "L7_GND")
|
||||
board.SetLayerType(pcbnew.In1_Cu, pcbnew.LT_POWER)
|
||||
board.SetLayerType(pcbnew.In3_Cu, pcbnew.LT_POWER)
|
||||
board.SetLayerType(pcbnew.In4_Cu, pcbnew.LT_SIGNAL)
|
||||
board.SetLayerType(pcbnew.In5_Cu, pcbnew.LT_POWER)
|
||||
board.SetLayerType(pcbnew.In6_Cu, pcbnew.LT_POWER)
|
||||
|
||||
|
||||
@@ -420,12 +473,20 @@ def add_mounting_holes(board: pcbnew.BOARD) -> None:
|
||||
|
||||
|
||||
def add_zone(board: pcbnew.BOARD, net_name: str, layer: int,
|
||||
bounds: tuple[float, float, float, float], priority: int = 0) -> None:
|
||||
bounds: tuple[float, float, float, float], priority: int = 0,
|
||||
clearance: float = 0.20) -> None:
|
||||
"""Add a retained solid inner-layer plane region.
|
||||
|
||||
These regions are retained even before fanout vias exist so the Specctra
|
||||
export contains real plane targets for the router. The final refill then
|
||||
clears anti-pads around every unrelated via and plated hole.
|
||||
|
||||
`clearance` decides whether a plane survives under the Ti375. Its escape
|
||||
vias sit on a 0.8-mm grid, so at the default 0.20 mm every anti-pad
|
||||
(0.225 + 0.20 = 0.425 mm radius) overlaps its neighbour and the plane is
|
||||
erased across the whole BGA footprint. At the board minimum of 0.10 mm the
|
||||
anti-pads are 0.325 mm and a 0.15-mm web survives between them, which is
|
||||
what lets an isolated bank-supply ball reach its rail at all.
|
||||
"""
|
||||
net = board.FindNet(net_name)
|
||||
if net is None:
|
||||
@@ -434,7 +495,7 @@ def add_zone(board: pcbnew.BOARD, net_name: str, layer: int,
|
||||
zone = pcbnew.ZONE(board)
|
||||
zone.SetLayer(layer)
|
||||
zone.SetNet(net)
|
||||
zone.SetLocalClearance(mm(0.20))
|
||||
zone.SetLocalClearance(mm(clearance))
|
||||
zone.SetMinThickness(mm(0.10))
|
||||
zone.SetThermalReliefGap(mm(0.20))
|
||||
zone.SetThermalReliefSpokeWidth(mm(0.25))
|
||||
@@ -449,69 +510,133 @@ def add_zone(board: pcbnew.BOARD, net_name: str, layer: int,
|
||||
|
||||
|
||||
def add_power_planes(board: pcbnew.BOARD) -> None:
|
||||
"""Ground on L2/L7, and the supply rails split across L4 and L6.
|
||||
|
||||
No rail owns a whole layer. L4 carries the core, the 5-V input rail, the
|
||||
always-on auxiliary rail and the four 3.0-V front-end rails; L6 carries the
|
||||
two large FPGA I/O rails. That leaves L3, L5 and B.Cu free for routing,
|
||||
which is where the Ti375's fanout has to go.
|
||||
"""
|
||||
# Continuous reference planes immediately below the two outer signal
|
||||
# layers. A 0.50-mm inset exceeds JLCPCB's copper-to-edge requirement.
|
||||
# These are the return path for every routed layer, so they use the tight
|
||||
# anti-pad clearance and stay continuous through the Ti375 via field. L2
|
||||
# in particular is the reference under the whole GPS front end and is never
|
||||
# split between "RF ground" and "digital ground".
|
||||
for layer in (pcbnew.In1_Cu, pcbnew.In6_Cu):
|
||||
add_zone(board, "GND", layer, (X0 + 0.50, Y0 + 0.50, X1 - 0.50, Y1 - 0.50))
|
||||
add_zone(board, "GND", layer, (X0 + 0.50, Y0 + 0.50, X1 - 0.50, Y1 - 0.50),
|
||||
clearance=PLANE_CLEARANCE)
|
||||
|
||||
# L4 is a deliberately split DC plane. Islands follow the functional
|
||||
# floorplan; no high-speed route uses L4 and no signal crosses a split on
|
||||
# either adjacent routing layer without a nearby return transition.
|
||||
islands = (
|
||||
("VCC3V3_AUX", (20.50, 38.00, 67.00, 72.00)),
|
||||
("VCC3V3", (39.00, 73.00, 68.00, 85.10)),
|
||||
("SYS5", (20.50, 85.50, 90.00, 109.50)),
|
||||
("SYS5", (90.40, 106.40, 140.00, 109.50)),
|
||||
# L4: everything except the two big I/O rails. Islands follow the
|
||||
# functional floorplan; no high-speed route uses L4 and no signal crosses a
|
||||
# split on either adjacent routing layer without a nearby return via.
|
||||
l4 = (
|
||||
("VCC3V3_AUX", (20.50, 36.00, 74.00, 72.00)),
|
||||
("VCC3V3_AUX", (53.00, 88.00, 70.00, 105.00)),
|
||||
("VCC3V3_AUX", (77.50, 84.00, 94.20, 100.50)),
|
||||
("VCC3V3_AUX", (133.00, 74.50, 152.00, 82.00)),
|
||||
("SYS5", (20.50, 85.50, 52.50, 109.50)),
|
||||
("SYS5", (89.50, 103.60, 145.00, 109.50)),
|
||||
# A corridor up the east edge carries 5 V to the antenna-bias block
|
||||
# instead of leaving those three pads stranded 55 mm from the rail.
|
||||
("SYS5", (140.00, 103.00, 174.50, 109.50)),
|
||||
("SYS5", (144.00, 86.50, 174.50, 104.00)),
|
||||
("SYS5", (157.00, 72.00, 174.50, 87.00)),
|
||||
("SYS5", (157.00, 63.50, 174.50, 72.50)),
|
||||
("VCC3V0_CLK", (126.00, 48.50, 156.00, 67.00)),
|
||||
("VCC3V0_RF", (140.00, 58.50, 150.50, 65.50)),
|
||||
("VCC3V0_DIG", (134.00, 62.50, 147.00, 74.00)),
|
||||
# Core balls are interspersed with GND/auxiliary balls, so a complete
|
||||
# local patch (with automatic anti-pads) replaces the invalid left/right
|
||||
# voltage split that previously crossed the BGA field.
|
||||
("VCC0V95", (95.00, 60.50, 119.00, 94.00)),
|
||||
("VCC3V3", (119.50, 87.00, 140.00, 106.00)),
|
||||
("VCC3V0_CLK", (126.00, 50.00, 148.50, 72.00)),
|
||||
("VCC3V0_DIG", (119.50, 60.50, 125.50, 72.00)),
|
||||
("VCC3V0_DIG", (148.90, 50.40, 164.00, 72.00)),
|
||||
("VCC3V3_ANT", (164.40, 50.40, 174.50, 70.00)),
|
||||
("VCC3V0_RF", (145.00, 36.00, 174.50, 50.00)),
|
||||
# local patch (with automatic anti-pads) replaces any voltage split
|
||||
# crossing the BGA field.
|
||||
("VCC0V95", (94.50, 56.00, 121.00, 103.00)),
|
||||
("VCC3V0_RF", (146.00, 39.80, 174.50, 50.50)),
|
||||
("VCC3V0_DIG", (149.00, 35.20, 161.00, 39.60)),
|
||||
("VCC3V0_DIG", (151.50, 44.80, 159.00, 48.60)),
|
||||
("VCC3V3_ANT", (162.00, 53.50, 174.50, 63.20)),
|
||||
)
|
||||
for priority, (net_name, bounds) in enumerate(islands, 1):
|
||||
add_zone(board, net_name, pcbnew.In3_Cu, bounds, priority)
|
||||
for priority, (net_name, bounds) in enumerate(l4, 1):
|
||||
# Only the core island sits under the BGA and needs the tight anti-pad.
|
||||
add_zone(board, net_name, pcbnew.In3_Cu, bounds, priority,
|
||||
clearance=PLANE_CLEARANCE if net_name == "VCC0V95" else 0.20)
|
||||
|
||||
# Local BGA supply patches. L2/L7 remain uninterrupted ground planes;
|
||||
# only the area below and immediately south of U1 is reserved on these
|
||||
# otherwise routable signal layers.
|
||||
add_zone(board, "VCC1V8", pcbnew.In4_Cu, (97.50, 60.50, 122.00, 101.00), 50)
|
||||
add_zone(board, "VCC3V3", pcbnew.In5_Cu, (97.50, 60.50, 140.00, 101.00), 50)
|
||||
# L6: the two FPGA I/O rails. VCC3V3 is the background because its loads
|
||||
# are spread over the whole west and the RF corner; VCC1V8 is carved out
|
||||
# where its own loads sit - under the BGA, along the 1.8-V PMOD headers,
|
||||
# over the configuration flash and at its converter output. The carve-outs
|
||||
# overlap each other so the rail stays one connected pour.
|
||||
l6 = (
|
||||
# Overlapping zones need distinct priorities even when they carry the
|
||||
# same net, so the carve-outs are numbered rather than sharing one.
|
||||
("VCC3V3", (X0 + 0.50, Y0 + 0.50, X1 - 0.50, Y1 - 0.50), 1),
|
||||
("VCC1V8", (99.00, 39.50, 122.00, 92.00), 10),
|
||||
("VCC1V8", (115.00, 20.50, 174.50, 40.50), 11),
|
||||
("VCC1V8", (94.00, 45.00, 106.00, 56.00), 12),
|
||||
("VCC1V8", (108.50, 91.50, 127.00, 106.00), 13),
|
||||
("VCC3V3", (128.00, 91.50, 141.00, 106.00), 20),
|
||||
)
|
||||
for net_name, bounds, priority in l6:
|
||||
add_zone(board, net_name, pcbnew.In5_Cu, bounds, priority,
|
||||
clearance=PLANE_CLEARANCE)
|
||||
|
||||
|
||||
# Areas the autorouter and the power stages must leave alone until
|
||||
# finalize_routing.py installs the audited RF and USB copper. The "below"
|
||||
# regions keep unrelated copper off the reference and routing layers under the
|
||||
# GPS front end and the USB pair; the "corridor" regions additionally reserve
|
||||
# F.Cu along the exact paths that finalize_routing.py later builds, since the
|
||||
# critical nets themselves are not part of the Specctra export.
|
||||
CRITICAL_KEEPOUTS_BELOW = (
|
||||
("TEMP_RF_NO_DIGITAL_BELOW", (152.00, 36.00, 174.50, 49.50)),
|
||||
("TEMP_USB_NO_CROSSING_BELOW", (26.00, 46.00, 39.60, 56.60)),
|
||||
)
|
||||
CRITICAL_KEEPOUTS_CORRIDOR = (
|
||||
# GPS chain: SMA launch, bias tee, DC block, SAW, series R and MIXIN_HI.
|
||||
("TEMP_RF_LINE", (154.40, 40.00, 174.60, 43.70)),
|
||||
# USB pair: receptacle breakout and the mirrored-contact crossover.
|
||||
("TEMP_USB_BREAKOUT", (27.30, 46.90, 30.65, 49.60)),
|
||||
# USB pair: vertical channel into the TPD2EUSB30A clamp, and the short
|
||||
# southward step D_P needs to clear the clamp's ground pad.
|
||||
("TEMP_USB_CHANNEL", (29.60, 49.60, 30.68, 52.60)),
|
||||
# USB pair: 45-degree run from the clamp to the series terminators.
|
||||
("TEMP_USB_DIAGONAL", (30.68, 52.20, 35.30, 56.00)),
|
||||
# USB pair: terminators and the matched legs into the FT4232H PHY.
|
||||
("TEMP_USB_TERMINATORS", (34.80, 54.35, 39.45, 55.60)),
|
||||
)
|
||||
|
||||
|
||||
def add_temporary_critical_keepouts(board: pcbnew.BOARD) -> None:
|
||||
"""Reserve the layers below RF and USB while Freerouting is running.
|
||||
"""Reserve the RF and USB geometry while the rest of the board is routed.
|
||||
|
||||
F.Cu deliberately remains available so the autorouted critical nets reserve
|
||||
their own top-layer corridors. The rule areas prevent unrelated signal
|
||||
tracks and through vias from crossing underneath. finalize_routing.py
|
||||
removes these temporary areas before installing the audited copper.
|
||||
finalize_routing.py removes every TEMP_ area before installing the audited
|
||||
copper, so nothing here reaches the manufactured board.
|
||||
"""
|
||||
layers = pcbnew.LSET()
|
||||
below = pcbnew.LSET()
|
||||
for layer in (pcbnew.In2_Cu, pcbnew.In4_Cu, pcbnew.In5_Cu, pcbnew.B_Cu):
|
||||
layers.AddLayer(layer)
|
||||
for name, bounds in (
|
||||
("TEMP_RF_NO_DIGITAL_BELOW", (152.0, 36.0, 174.5, 49.5)),
|
||||
("TEMP_USB_NO_CROSSING_BELOW", (22.0, 41.5, 44.5, 57.5))):
|
||||
x0, y0, x1, y1 = bounds
|
||||
area = pcbnew.ZONE(board)
|
||||
area.SetZoneName(name)
|
||||
area.SetIsRuleArea(True)
|
||||
area.SetLayerSet(layers)
|
||||
area.SetDoNotAllowTracks(True)
|
||||
area.SetDoNotAllowVias(True)
|
||||
area.SetDoNotAllowPads(False)
|
||||
area.SetDoNotAllowFootprints(False)
|
||||
area.SetDoNotAllowZoneFills(False)
|
||||
outline = area.Outline()
|
||||
outline.NewOutline()
|
||||
for x, y in ((x0, y0), (x1, y0), (x1, y1), (x0, y1)):
|
||||
outline.Append(point(x, y))
|
||||
board.Add(area)
|
||||
below.AddLayer(layer)
|
||||
corridor = pcbnew.LSET()
|
||||
for layer in (pcbnew.F_Cu, pcbnew.In2_Cu, pcbnew.In4_Cu,
|
||||
pcbnew.In5_Cu, pcbnew.B_Cu):
|
||||
corridor.AddLayer(layer)
|
||||
|
||||
for layers, group in ((below, CRITICAL_KEEPOUTS_BELOW),
|
||||
(corridor, CRITICAL_KEEPOUTS_CORRIDOR)):
|
||||
for name, bounds in group:
|
||||
x0, y0, x1, y1 = bounds
|
||||
area = pcbnew.ZONE(board)
|
||||
area.SetZoneName(name)
|
||||
area.SetIsRuleArea(True)
|
||||
area.SetLayerSet(layers)
|
||||
area.SetDoNotAllowTracks(True)
|
||||
area.SetDoNotAllowVias(True)
|
||||
area.SetDoNotAllowPads(False)
|
||||
area.SetDoNotAllowFootprints(False)
|
||||
area.SetDoNotAllowZoneFills(False)
|
||||
outline = area.Outline()
|
||||
outline.NewOutline()
|
||||
for x, y in ((x0, y0), (x1, y0), (x1, y1), (x0, y1)):
|
||||
outline.Append(point(x, y))
|
||||
board.Add(area)
|
||||
|
||||
|
||||
def add_documentation_graphics(board: pcbnew.BOARD) -> None:
|
||||
@@ -547,7 +672,7 @@ STACKUP = '''\t\t(stackup
|
||||
\t\t\t(layer "dielectric 4" (type "core") (thickness 0.3) (material "JLC FR-4 core") (epsilon_r 4.41) (loss_tangent 0.02))
|
||||
\t\t\t(layer "L5_SIG" (type "copper") (thickness 0.0152))
|
||||
\t\t\t(layer "dielectric 5" (type "prepreg") (thickness 0.3568) (material "JLC 3x2116") (epsilon_r 4.16) (loss_tangent 0.02))
|
||||
\t\t\t(layer "L6_SIG" (type "copper") (thickness 0.0152))
|
||||
\t\t\t(layer "L6_PWR" (type "copper") (thickness 0.0152))
|
||||
\t\t\t(layer "dielectric 6" (type "core") (thickness 0.1) (material "JLC FR-4 core") (epsilon_r 4.36) (loss_tangent 0.02))
|
||||
\t\t\t(layer "L7_GND" (type "copper") (thickness 0.0152))
|
||||
\t\t\t(layer "dielectric 7" (type "prepreg") (thickness 0.0994) (material "JLC 3313") (epsilon_r 4.1) (loss_tangent 0.02))
|
||||
@@ -566,8 +691,6 @@ def inject_stackup(path: Path) -> None:
|
||||
data = re.sub(r"\n\t\t\(stackup\n.*?\n\t\t\)\n", "\n" + STACKUP + "\n", data, count=1, flags=re.S)
|
||||
else:
|
||||
data = data.replace("\n\t(setup\n", "\n\t(setup\n" + STACKUP + "\n", 1)
|
||||
data = data.replace("(allow_soldermask_bridges_in_footprints no)",
|
||||
"(allow_soldermask_bridges_in_footprints yes)")
|
||||
path.write_text(data)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user