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 @@ -38,6 +38,15 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}

override _step() {
if (
!this.layout &&
this.partitionInputProblem.partitionType === "decoupling_caps"
) {
this.layout = this.createDecouplingCapsLayout()
this.solved = true
return
}

// Initialize PackSolver2 if not already created
if (!this.activeSubSolver) {
const packInput = this.createPackInput()
Expand All @@ -64,6 +73,81 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}
}

private createDecouplingCapsLayout(): OutputLayout {
const gap =
this.partitionInputProblem.decouplingCapsGap ??
this.partitionInputProblem.chipGap
const chipIds = Object.keys(this.partitionInputProblem.chipMap).sort(
naturalCompare,
)
const capItems = chipIds.map((chipId) => {
const chip = this.partitionInputProblem.chipMap[chipId]!
const rotation = this.getPreferredDecouplingCapRotation(chipId)
const footprint =
rotation === 90 || rotation === 270
? { x: chip.size.y, y: chip.size.x }
: chip.size

return { chipId, rotation, footprint }
})

const totalWidth =
capItems.reduce((sum, item) => sum + item.footprint.x, 0) +
Math.max(0, capItems.length - 1) * gap
let cursorX = -totalWidth / 2
const chipPlacements: Record<string, Placement> = {}

for (const item of capItems) {
chipPlacements[item.chipId] = {
x: cursorX + item.footprint.x / 2,
y: 0,
ccwRotationDegrees: item.rotation,
}
cursorX += item.footprint.x + gap
}

return {
chipPlacements,
groupPlacements: {},
}
}

private getPreferredDecouplingCapRotation(
chipId: ChipId,
): 0 | 90 | 180 | 270 {
const chip = this.partitionInputProblem.chipMap[chipId]!
const allowedRotations = chip.availableRotations ?? [0, 90, 180, 270]
const fallback = allowedRotations[0] ?? 0
const positiveVoltagePin = chip.pins.find((pinId) =>
this.isPinConnectedToPositiveVoltage(pinId),
)

if (!positiveVoltagePin) return fallback

const pin = this.partitionInputProblem.chipPinMap[positiveVoltagePin]
if (!pin) return fallback

const preferredRotation = pin.side === "y-" ? 180 : 0
return allowedRotations.includes(preferredRotation)
? preferredRotation
: fallback
}

private isPinConnectedToPositiveVoltage(pinId: PinId): boolean {
for (const [connKey, connected] of Object.entries(
this.partitionInputProblem.netConnMap,
)) {
if (!connected) continue
const [connectedPinId, netId] = connKey.split("-") as [PinId, NetId]
if (connectedPinId !== pinId) continue
if (this.partitionInputProblem.netMap[netId]?.isPositiveVoltageSource) {
return true
}
}

return false
}

private createPackInput(): PackInput {
// Fall back to filtered mapping (weak + strong)
const pinToNetworkMap = createFilteredNetworkMapping({
Expand Down Expand Up @@ -182,3 +266,7 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
return [this.partitionInputProblem]
}
}

function naturalCompare(a: string, b: string): number {
return a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" })
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@types/bun": "latest",
"bpc-graph": "^0.0.66",
"calculate-packing": "^0.0.31",
"circuit-to-svg": "0.0.345",
"circuit-json": "^0.0.226",
"graphics-debug": "^0.0.64",
"react-cosmos": "^7.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { expect, test } from "bun:test"
import { SingleInnerPartitionPackingSolver } from "../../lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver"
import type { PartitionInputProblem } from "../../lib/types/InputProblem"

function createDecouplingCapsPartition(): PartitionInputProblem {
return {
chipMap: {
C1: {
chipId: "C1",
pins: ["C1.1", "C1.2"],
size: { x: 0.8, y: 0.4 },
availableRotations: [0, 180],
},
C2: {
chipId: "C2",
pins: ["C2.1", "C2.2"],
size: { x: 1.2, y: 0.4 },
availableRotations: [0, 180],
},
C10: {
chipId: "C10",
pins: ["C10.1", "C10.2"],
size: { x: 1, y: 0.4 },
availableRotations: [0, 180],
},
},
chipPinMap: {
"C1.1": { pinId: "C1.1", offset: { x: 0, y: -0.2 }, side: "y-" },
"C1.2": { pinId: "C1.2", offset: { x: 0, y: 0.2 }, side: "y+" },
"C2.1": { pinId: "C2.1", offset: { x: 0, y: 0.2 }, side: "y+" },
"C2.2": { pinId: "C2.2", offset: { x: 0, y: -0.2 }, side: "y-" },
"C10.1": { pinId: "C10.1", offset: { x: 0, y: 0.2 }, side: "y+" },
"C10.2": { pinId: "C10.2", offset: { x: 0, y: -0.2 }, side: "y-" },
},
netMap: {
GND: { netId: "GND", isGround: true },
VDD: { netId: "VDD", isPositiveVoltageSource: true },
},
pinStrongConnMap: {},
netConnMap: {
"C1.1-VDD": true,
"C1.2-GND": true,
"C2.1-VDD": true,
"C2.2-GND": true,
"C10.1-VDD": true,
"C10.2-GND": true,
},
chipGap: 0.2,
partitionGap: 1,
decouplingCapsGap: 0.3,
isPartition: true,
partitionType: "decoupling_caps",
}
}

test("SingleInnerPartitionPackingSolver centers decoupling caps in a deterministic row", () => {
const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem: createDecouplingCapsPartition(),
pinIdToStronglyConnectedPins: {},
})

solver.step()

expect(solver.solved).toBe(true)
expect(solver.failed).toBe(false)
expect(solver.activeSubSolver).toBeUndefined()
expect(Object.keys(solver.layout!.chipPlacements)).toEqual([
"C1",
"C2",
"C10",
])
expect(solver.layout!.chipPlacements.C1!.x).toBeCloseTo(-1.4)
expect(solver.layout!.chipPlacements.C2!.x).toBeCloseTo(-0.1)
expect(solver.layout!.chipPlacements.C10!.x).toBeCloseTo(1.3)
expect(solver.layout!.chipPlacements.C1!.y).toBe(0)
expect(solver.layout!.chipPlacements.C2!.y).toBe(0)
expect(solver.layout!.chipPlacements.C10!.y).toBe(0)
})

test("SingleInnerPartitionPackingSolver rotates caps so positive voltage pins face y+", () => {
const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem: createDecouplingCapsPartition(),
pinIdToStronglyConnectedPins: {},
})

solver.step()

expect(solver.layout!.chipPlacements.C1!.ccwRotationDegrees).toBe(180)
expect(solver.layout!.chipPlacements.C2!.ccwRotationDegrees).toBe(0)
expect(solver.layout!.chipPlacements.C10!.ccwRotationDegrees).toBe(0)
})
Loading