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
76 changes: 73 additions & 3 deletions lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export class ChipPartitionsSolver extends BaseSolver {

// 1) Build decoupling-cap-only partitions (exclude the main chip for each group)
const decapChipIdSet = new Set<ChipId>()
const decapGroupPartitions: ChipId[][] = []
const decapGroupPartitions: Array<{
chipIds: ChipId[]
netPair: [NetId, NetId]
}> = []

if (this.decouplingCapGroups && this.decouplingCapGroups.length > 0) {
for (const group of this.decouplingCapGroups) {
Expand All @@ -61,7 +64,10 @@ export class ChipPartitionsSolver extends BaseSolver {
}
// Only add a partition if there are at least two caps present in the inputProblem
if (capsOnly.length >= 2) {
decapGroupPartitions.push(capsOnly)
decapGroupPartitions.push({
chipIds: capsOnly,
netPair: group.netPair,
})
// Mark these caps as handled by decoupling-cap partitions
for (const capId of capsOnly) {
decapChipIdSet.add(capId)
Expand Down Expand Up @@ -119,8 +125,9 @@ export class ChipPartitionsSolver extends BaseSolver {

return [
...decapGroupPartitions.map((partition) =>
this.createInputProblemFromPartition(partition, inputProblem, {
this.createInputProblemFromPartition(partition.chipIds, inputProblem, {
partitionType: "decoupling_caps",
decouplingCapNetPair: partition.netPair,
}),
),
...nonDecapPartitions.map((partition) =>
Expand Down Expand Up @@ -188,6 +195,7 @@ export class ChipPartitionsSolver extends BaseSolver {
originalProblem: InputProblem,
opts?: {
partitionType?: "default" | "decoupling_caps"
decouplingCapNetPair?: [NetId, NetId]
},
): PartitionInputProblem {
const chipIds = partition
Expand Down Expand Up @@ -242,6 +250,19 @@ export class ChipPartitionsSolver extends BaseSolver {
}
}

if (
opts?.partitionType === "decoupling_caps" &&
opts.decouplingCapNetPair
) {
this.addExternalDecouplingNetConnections({
originalProblem,
relevantPinIds,
allowedNetIds: new Set(opts.decouplingCapNetPair),
relevantNetIds,
netConnMap,
})
}

for (const netId of relevantNetIds) {
if (originalProblem.netMap[netId]) {
netMap[netId] = originalProblem.netMap[netId]
Expand All @@ -260,6 +281,55 @@ export class ChipPartitionsSolver extends BaseSolver {
}
}

private addExternalDecouplingNetConnections({
originalProblem,
relevantPinIds,
allowedNetIds,
relevantNetIds,
netConnMap,
}: {
originalProblem: InputProblem
relevantPinIds: Set<PinId>
allowedNetIds: Set<NetId>
relevantNetIds: Set<NetId>
netConnMap: Record<string, boolean>
}) {
const copyNetsFromExternalPin = (
innerPinId: PinId,
externalPinId: PinId,
) => {
for (const [connKey, isConnected] of Object.entries(
originalProblem.netConnMap,
)) {
if (!isConnected) continue

const [pinId, netId] = connKey.split("-") as [PinId, NetId]
if (pinId !== externalPinId || !allowedNetIds.has(netId)) continue

relevantNetIds.add(netId)
netConnMap[`${innerPinId}-${netId}`] = true
}
}

for (const [connKey, isConnected] of Object.entries(
originalProblem.pinStrongConnMap,
)) {
if (!isConnected) continue

const [pinAId, pinBId] = connKey.split("-") as [PinId, PinId]
const pinAIsRelevant = relevantPinIds.has(pinAId)
const pinBIsRelevant = relevantPinIds.has(pinBId)

if (pinAIsRelevant === pinBIsRelevant) continue

if (pinAIsRelevant) {
copyNetsFromExternalPin(pinAId, pinBId)
} else {
copyNetsFromExternalPin(pinBId, pinAId)
}
}
}

override visualize(): GraphicsObject {
if (this.partitions.length === 0) {
return super.visualize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
NetId,
ChipPin,
PartitionInputProblem,
Chip,
} from "../../types/InputProblem"
import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem"
import { createFilteredNetworkMapping } from "../../utils/networkFiltering"
Expand All @@ -38,6 +39,16 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}

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

// Initialize PackSolver2 if not already created
if (!this.activeSubSolver) {
const packInput = this.createPackInput()
Expand Down Expand Up @@ -141,6 +152,62 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver {
}
}

private createDecouplingCapsLayout(): OutputLayout {
const gap =
this.partitionInputProblem.decouplingCapsGap ??
this.partitionInputProblem.chipGap
const caps = Object.entries(this.partitionInputProblem.chipMap)
.sort(([chipAId], [chipBId]) =>
chipAId.localeCompare(chipBId, undefined, { numeric: true }),
)
.map(([chipId, chip]) => {
const ccwRotationDegrees = this.pickDecouplingCapRotation(chip)
const size = this.getRotatedChipSize(chip, ccwRotationDegrees)

return {
chipId,
ccwRotationDegrees,
width: size.x,
}
})

const totalWidth =
caps.reduce((sum, cap) => sum + cap.width, 0) +
Math.max(0, caps.length - 1) * gap
let nextLeftEdge = -totalWidth / 2
const chipPlacements: Record<string, Placement> = {}

for (const cap of caps) {
chipPlacements[cap.chipId] = {
x: nextLeftEdge + cap.width / 2,
y: 0,
ccwRotationDegrees: cap.ccwRotationDegrees,
}
nextLeftEdge += cap.width + gap
}

return {
chipPlacements,
groupPlacements: {},
}
}

private pickDecouplingCapRotation(chip: Chip): 0 | 90 | 180 | 270 {
const availableRotations = chip.availableRotations ?? [0, 90, 180, 270]
for (const rotation of [0, 180, 90, 270] as const) {
if (availableRotations.includes(rotation)) return rotation
}
return availableRotations[0] ?? 0
}

private getRotatedChipSize(chip: Chip, rotation: 0 | 90 | 180 | 270) {
if (rotation === 90 || rotation === 270) {
return { x: chip.size.y, y: chip.size.x }
}

return chip.size
}

private createLayoutFromPackingResult(
packedComponents: PackSolver2["packedComponents"],
): OutputLayout {
Expand Down
13 changes: 11 additions & 2 deletions pages/LayoutPipelineSolver/LayoutPipelineSolver06.page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { PackInput } from "calculate-packing"
import { LayoutPipelineDebugger } from "lib/components/LayoutPipelineDebugger"
import type { InputProblem } from "lib/index"
import { lazy, Suspense } from "react"

const LayoutPipelineDebugger = lazy(async () => {
const mod = await import("lib/components/LayoutPipelineDebugger")
return { default: mod.LayoutPipelineDebugger }
})

export const problem: InputProblem = {
chipMap: {
Expand Down Expand Up @@ -877,5 +882,9 @@ export const problem: InputProblem = {
}

export default function LayoutPipelineSolver06Page() {
return <LayoutPipelineDebugger problem={problem} />
return (
<Suspense fallback={null}>
<LayoutPipelineDebugger problem={problem} />
</Suspense>
)
}
99 changes: 99 additions & 0 deletions tests/ChipPartitionsSolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,102 @@ test("ChipPartitionsSolver visualization contains partition components", () => {
expect(visualization.rects?.length).toBeGreaterThan(0)
expect(visualization.texts?.length).toBeGreaterThan(0)
})

test("ChipPartitionsSolver keeps decoupling cap nets inherited through main-chip strong connections", () => {
const inputProblem: InputProblem = {
chipMap: {
U1: {
chipId: "U1",
pins: ["U1.VCC", "U1.GND"],
size: { x: 2, y: 2 },
},
C1: {
chipId: "C1",
pins: ["C1.1", "C1.2"],
size: { x: 0.8, y: 0.4 },
},
C2: {
chipId: "C2",
pins: ["C2.1", "C2.2"],
size: { x: 0.8, y: 0.4 },
},
},
chipPinMap: {
"U1.VCC": {
pinId: "U1.VCC",
offset: { x: 1, y: 0.5 },
side: "x+",
},
"U1.GND": {
pinId: "U1.GND",
offset: { x: -1, y: -0.5 },
side: "x-",
},
"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-",
},
},
netMap: {
VCC: { netId: "VCC", isPositiveVoltageSource: true },
GND: { netId: "GND", isGround: true },
},
pinStrongConnMap: {
"C1.1-U1.VCC": true,
"U1.VCC-C1.1": true,
"C1.2-U1.GND": true,
"U1.GND-C1.2": true,
"C2.1-U1.VCC": true,
"U1.VCC-C2.1": true,
"C2.2-U1.GND": true,
"U1.GND-C2.2": true,
},
netConnMap: {
"U1.VCC-VCC": true,
"U1.GND-GND": true,
},
chipGap: 0.2,
partitionGap: 2,
}

const solver = new ChipPartitionsSolver({
inputProblem,
decouplingCapGroups: [
{
decouplingCapGroupId: "decap_U1_VCC_GND",
mainChipId: "U1",
netPair: ["GND", "VCC"],
decouplingCapChipIds: ["C1", "C2"],
},
],
})
solver.solve()

const decapPartition = solver.partitions.find(
(partition) => partition.partitionType === "decoupling_caps",
)

expect(decapPartition).toBeDefined()
expect(Object.keys(decapPartition!.chipMap).sort()).toEqual(["C1", "C2"])
expect(Object.keys(decapPartition!.netMap).sort()).toEqual(["GND", "VCC"])
expect(decapPartition!.netConnMap["C1.1-VCC"]).toBe(true)
expect(decapPartition!.netConnMap["C1.2-GND"]).toBe(true)
expect(decapPartition!.netConnMap["C2.1-VCC"]).toBe(true)
expect(decapPartition!.netConnMap["C2.2-GND"]).toBe(true)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { expect, test } from "bun:test"
import { SingleInnerPartitionPackingSolver } from "../../lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver"
import type { PartitionInputProblem } from "../../lib/types/InputProblem"

test("SingleInnerPartitionPackingSolver lays decoupling caps out in a centered deterministic row", () => {
const partitionInputProblem: PartitionInputProblem = {
chipMap: {
C10: {
chipId: "C10",
pins: [],
size: { x: 2, y: 1 },
availableRotations: [0],
},
C2: {
chipId: "C2",
pins: [],
size: { x: 1, y: 1 },
availableRotations: [0],
},
C1: {
chipId: "C1",
pins: [],
size: { x: 3, y: 1 },
availableRotations: [0],
},
},
chipPinMap: {},
netMap: {},
pinStrongConnMap: {},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
decouplingCapsGap: 0.5,
partitionType: "decoupling_caps",
isPartition: true,
}

const solver = new SingleInnerPartitionPackingSolver({
partitionInputProblem,
pinIdToStronglyConnectedPins: {},
})

solver.step()

expect(solver.solved).toBe(true)
expect(solver.failed).toBe(false)
expect(solver.activeSubSolver).toBeNull()
expect(solver.layout?.chipPlacements.C1).toEqual({
x: -2,
y: 0,
ccwRotationDegrees: 0,
})
expect(solver.layout?.chipPlacements.C2).toEqual({
x: 0.5,
y: 0,
ccwRotationDegrees: 0,
})
expect(solver.layout?.chipPlacements.C10).toEqual({
x: 2.5,
y: 0,
ccwRotationDegrees: 0,
})
})
Loading