Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,15 @@ export class GlobalDrcBranchPortfolioSolver extends BaseSolver {
this.params.connMap,
this.autoroutingDrcEngine,
)
// Match the inner solver's staged scoring: via-pad issues are handled
// by the following phase, while via-pair/trace collisions stay guarded.
const inputViaIssueCount = getViaDrcIssueCount(
this.safeTraceLayerInputSnapshot!,
false,
)
const safeTraceLayerViaIssueCount = getViaDrcIssueCount(
safeTraceLayerSnapshot,
false,
)
this.safeTraceLayerPhaseAccepted =
safeTraceLayerViaIssueCount <= inputViaIssueCount &&
Expand All @@ -506,6 +510,7 @@ export class GlobalDrcBranchPortfolioSolver extends BaseSolver {
this.safeTraceLayerInputSnapshot!.count,
this.safeTraceLayerInputSnapshot!.issueScore,
inputViaIssueCount,
this.safeTraceLayerInputSnapshot!,
)
const acceptedRoutes = this.safeTraceLayerPhaseAccepted
? safeTraceLayerRoutes
Expand Down
12 changes: 5 additions & 7 deletions lib/utils/getViaLayers.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { mapZToLayerName } from "./mapZToLayerName"

type ViaLayers = {
layers?: string[]
from_layer?: string
to_layer?: string
type ViaSpan = {
from_layer: string
to_layer: string
}

/** Normalize the explicit-layer and inclusive-endpoint representations. */
export const getViaLayers = (via: ViaLayers, layerCount: number): string[] => {
if (via.layers !== undefined) return via.layers
/** Expand via endpoints into the inclusive span of board layers. */
export const getViaLayers = (via: ViaSpan, layerCount: number): string[] => {
if (!Number.isInteger(layerCount) || layerCount < 1) {
throw new Error(`Invalid board layer count: ${layerCount}`)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AutoroutingDrcEngine } from "../lib/drc/AutoroutingDrcEngine"
import type { SimpleRouteJson, SimplifiedPcbTraces } from "../lib/types"
import { convertToCircuitJson } from "../lib/utils/convertToCircuitJson"

test("DRC treats endpoint spans and explicit via layers as the same geometry", () => {
test("DRC checks forward and reversed via endpoint spans identically", () => {
const srj: SimpleRouteJson = {
layerCount: 4,
minTraceWidth: 0.12,
Expand Down Expand Up @@ -42,17 +42,18 @@ test("DRC treats endpoint spans and explicit via layers as the same geometry", (
const declared = structuredClone(traces)
const via = declared[0]!.route[0]!
if (via.route_type !== "via") throw new Error("Expected a via")
via.layers = ["top", "inner1", "inner2"]
via.from_layer = "inner2"
via.to_layer = "top"
expect(engine.evaluate(declared).errors).toEqual(
engine.evaluate(traces).errors,
)
via.layers = ["top", "inner1", "inner2", "bottom"]
via.from_layer = "bottom"
expect(engine.evaluate(declared).errors.length).toBeGreaterThan(0)
for (const point of declared[1]!.route) {
if (point.route_type === "wire") point.layer = "bottom"
}
expect(engine.evaluate(declared).errors.length).toBeGreaterThan(0)
via.layers = ["top", "inner1", "inner2"]
via.from_layer = "inner2"
expect(engine.evaluate(declared).errors).toEqual([])
const json = convertToCircuitJson(srj, declared)
const exportedVia = json.find((element) => element.type === "pcb_via")
Expand Down
3 changes: 2 additions & 1 deletion tests/autorouting-drc-via-span-pad.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ test("checks via-to-pad clearance on intermediate layers only within the span",

const via = traces[0]!.route[0]!
if (via.route_type !== "via") throw new Error("Expected a via")
via.layers = ["top", "inner1", "inner2"]
via.from_layer = "inner2"
via.to_layer = "top"
expect(new AutoroutingDrcEngine(srj).evaluate(traces).errors).toEqual(errors)
srj.obstacles[0]!.layers = ["bottom"]
expect(new AutoroutingDrcEngine(srj).evaluate(traces).errors).toEqual([])
Expand Down
95 changes: 95 additions & 0 deletions tests/trace-layer-phase-via-pad-selection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { expect, test } from "bun:test"
import {
GlobalDrcBranchPortfolioSolver,
type DrcEvaluator,
type HighDensityRoute,
type SimpleRouteJson,
} from "../lib"

test("keeps trace-layer improvements for via-pad repair without accepting via-trace regressions", () => {
const srj: SimpleRouteJson = {
layerCount: 3,
minTraceWidth: 0.1,
minViaDiameter: 0.3,
bounds: { minX: -3, minY: -3, maxX: 3, maxY: 3 },
obstacles: [],
connections: [
{
name: "trace",
pointsToConnect: [
{ x: -2, y: 0, layer: "top" },
{ x: 2, y: 0, layer: "top" },
],
},
{
name: "foreign",
pointsToConnect: [
{ x: 0, y: -2, layer: "top" },
{ x: 0, y: 2, layer: "top" },
],
},
],
}
const hdRoutes: HighDensityRoute[] = srj.connections.map((connection) => ({
connectionName: connection.name,
traceThickness: 0.1,
viaDiameter: 0.3,
vias: [],
route: connection.pointsToConnect.map((point) => ({
x: point.x,
y: point.y,
z: 0,
})),
}))
const initialError = {
type: "pcb_trace_error",
pcb_trace_id: "trace_0",
pcb_trace_error_id: "overlap_trace_0_foreign_0",
center: { x: 0, y: 0 },
}
for (const errorType of [
"pcb_pad_pad_clearance_error",
"pcb_via_trace_clearance_error",
]) {
const residualError = {
type: errorType,
pcb_via_ids: ["trace_via_0"],
center: { x: -2, y: 0 },
}
const drcEvaluator: DrcEvaluator = ({ routes, hdRoutes }) => {
const candidateRoutes = hdRoutes ?? routes
if (!candidateRoutes) throw new Error("Expected candidate routes")
return candidateRoutes.some((route) =>
route.route.some((point) => point.z !== 0),
)
? [residualError]
: [initialError]
}
const solver = new GlobalDrcBranchPortfolioSolver({
srj,
hdRoutes,
drcEvaluator,
maxIterations: 2,
broadMaxIterations: 1,
broadPassMultiplier: 1,
viaInPadMaxIterations: 1,
enableLargeBoardBroadFallback: false,
enablePostSolveClearanceRelaxation: false,
enableSafeTraceLayerMoves: true,
enableViaInPadLayerMoves: false,
})
solver.solve()

expect(solver.solved).toBe(true)
const shouldAccept = errorType === "pcb_pad_pad_clearance_error"
expect(solver.stats.drcBranchPortfolioSafeTraceLayerPhaseAccepted).toBe(
shouldAccept,
)
expect(drcEvaluator({ traces: [], hdRoutes: solver.getOutput() })).toEqual([
shouldAccept ? residualError : initialError,
])
}
expect(
hdRoutes.every((route) => route.route.every((point) => point.z === 0)),
).toBe(true)
})
3 changes: 1 addition & 2 deletions tests/via-layer-span.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { expect, test } from "bun:test"
import { getViaLayers } from "../lib/utils/getViaLayers"

test("normalizes both via APIs across the complete inclusive layer span", () => {
test("expands via endpoints across the complete inclusive layer span", () => {
const layers = ["top", "inner1", "inner2", "bottom"]
expect(getViaLayers({ layers }, 4)).toBe(layers)
expect(getViaLayers({ from_layer: "top", to_layer: "bottom" }, 4)).toEqual(
layers,
)
Expand Down
2 changes: 0 additions & 2 deletions types/srj-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ export interface SimplifiedPcbTrace {
to_layer: string
from_layer: string
via_diameter?: number
/** Explicit copper layers; otherwise expand the inclusive from/to span. */
layers?: string[]
}
| {
route_type: "jumper"
Expand Down
Loading