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
65 changes: 56 additions & 9 deletions lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,35 @@
* Divides the layout problem into manageable sections for more efficient processing.
*/

import { BaseSolver } from "../BaseSolver"
import type { GraphicsObject } from "graphics-debug"
import { stackGraphicsHorizontally } from "graphics-debug"
import { doBasicInputProblemLayout } from "lib/solvers/LayoutPipelineSolver/doBasicInputProblemLayout"
import { visualizeInputProblem } from "lib/solvers/LayoutPipelineSolver/visualizeInputProblem"
import type {
InputProblem,
Chip,
ChipId,
PinId,
InputProblem,
NetId,
PartitionInputProblem,
PinId,
} from "lib/types/InputProblem"
import type { GraphicsObject } from "graphics-debug"
import { stackGraphicsHorizontally } from "graphics-debug"
import { visualizeInputProblem } from "lib/solvers/LayoutPipelineSolver/visualizeInputProblem"
import { doBasicInputProblemLayout } from "lib/solvers/LayoutPipelineSolver/doBasicInputProblemLayout"
import { BaseSolver } from "../BaseSolver"
import type { DecouplingCapGroup } from "../IdentifyDecouplingCapsSolver/IdentifyDecouplingCapsSolver"

const LARGE_CHIP_PIN_COUNT = 4
const SMALL_SUPPORT_COMPONENT_MAX_PIN_COUNT = 3
const SMALL_SUPPORT_COMPONENT_PREFIXES = new Set([
"R",
"C",
"L",
"D",
"LED",
"FB",
"F",
"SJ",
"JP",
])

export class ChipPartitionsSolver extends BaseSolver {
inputProblem: InputProblem
partitions: PartitionInputProblem[] = []
Expand Down Expand Up @@ -99,8 +114,13 @@ export class ChipPartitionsSolver extends BaseSolver {
!decapChipIdSet.has(owner1) &&
!decapChipIdSet.has(owner2)
) {
adjacencyMap.get(owner1)!.add(owner2)
adjacencyMap.get(owner2)!.add(owner1)
const chip1 = inputProblem.chipMap[owner1]!
const chip2 = inputProblem.chipMap[owner2]!

if (this.shouldConnectChipsInPartition(chip1, chip2)) {
adjacencyMap.get(owner1)!.add(owner2)
adjacencyMap.get(owner2)!.add(owner1)
}
}
}

Expand Down Expand Up @@ -150,6 +170,33 @@ export class ChipPartitionsSolver extends BaseSolver {
return null
}

private shouldConnectChipsInPartition(chip1: Chip, chip2: Chip): boolean {
const chip1IsLarge = chip1.pins.length >= LARGE_CHIP_PIN_COUNT
const chip2IsLarge = chip2.pins.length >= LARGE_CHIP_PIN_COUNT
const chip1IsSmallSupport = this.isSmallSupportComponent(chip1)
const chip2IsSmallSupport = this.isSmallSupportComponent(chip2)

return !(
(chip1IsLarge && chip2IsSmallSupport) ||
(chip2IsLarge && chip1IsSmallSupport)
)
}

private isSmallSupportComponent(chip: Chip): boolean {
if (chip.pins.length > SMALL_SUPPORT_COMPONENT_MAX_PIN_COUNT) {
return false
}

const prefix = this.getRefdesPrefix(chip.chipId)
return prefix !== null && SMALL_SUPPORT_COMPONENT_PREFIXES.has(prefix)
}

private getRefdesPrefix(chipId: ChipId): string | null {
const name = chipId.split(/[/.]/).pop() ?? chipId
const match = name.toUpperCase().match(/^[A-Z]+/)
return match?.[0] ?? null
}

/**
* Depth-first search to find connected components
*/
Expand Down
57 changes: 38 additions & 19 deletions lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* Combines all the individually processed partitions into the final schematic layout.
*/

import type { GraphicsObject } from "graphics-debug"
import { type PackInput, PackSolver2 } from "calculate-packing"
import { BaseSolver } from "../BaseSolver"
import type { GraphicsObject } from "graphics-debug"
import type { InputProblem, NetId, PinId } from "../../types/InputProblem"
import type { OutputLayout, Placement } from "../../types/OutputLayout"
import type { InputProblem, PinId, NetId } from "../../types/InputProblem"
import { BaseSolver } from "../BaseSolver"
import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem"
import type { PackedPartition } from "../PackInnerPartitionsSolver/PackInnerPartitionsSolver"

Expand Down Expand Up @@ -99,30 +99,48 @@ export class PartitionPackingSolver extends BaseSolver {

private buildConnectivityMap(): Map<PinId, NetId> {
const pinToNetworkMap = new Map<PinId, NetId>()
const addNetConnection = (connKey: string, connected: boolean) => {
if (!connected) return
const [pinId, netId] = connKey.split("-")
if (pinId && netId) pinToNetworkMap.set(pinId, netId)
}
const addStrongConnection = (connKey: string, connected: boolean) => {
if (!connected) return
const pins = connKey.split("-")
if (pins.length === 2 && pins[0] && pins[1]) {
const existingNet =
pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1])
if (existingNet) {
pinToNetworkMap.set(pins[0], existingNet)
pinToNetworkMap.set(pins[1], existingNet)
} else {
pinToNetworkMap.set(pins[0], connKey)
pinToNetworkMap.set(pins[1], connKey)
}
}
}

for (const [connKey, connected] of Object.entries(
this.inputProblem.netConnMap,
)) {
addNetConnection(connKey, connected)
}
for (const [connKey, connected] of Object.entries(
this.inputProblem.pinStrongConnMap,
)) {
addStrongConnection(connKey, connected)
}

for (const packedPartition of this.packedPartitions) {
for (const [connKey, connected] of Object.entries(
packedPartition.inputProblem.netConnMap,
)) {
if (!connected) continue
const [pinId, netId] = connKey.split("-")
if (pinId && netId) pinToNetworkMap.set(pinId, netId)
addNetConnection(connKey, connected)
}
for (const [connKey, connected] of Object.entries(
packedPartition.inputProblem.pinStrongConnMap,
)) {
if (!connected) continue
const pins = connKey.split("-")
if (pins.length === 2 && pins[0] && pins[1]) {
const existingNet =
pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1])
if (existingNet) {
pinToNetworkMap.set(pins[0], existingNet)
pinToNetworkMap.set(pins[1], existingNet)
} else {
pinToNetworkMap.set(pins[0], connKey)
pinToNetworkMap.set(pins[1], connKey)
}
}
addStrongConnection(connKey, connected)
}
}
return pinToNetworkMap
Expand Down Expand Up @@ -279,6 +297,7 @@ export class PartitionPackingSolver extends BaseSolver {
for (const packedComponent of packedComponents) {
const partitionIndex = parseInt(
packedComponent.componentId.replace("partition_", ""),
10,
)
const group = partitionGroups.find(
(g) => g.partitionIndex === partitionIndex,
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,9 @@
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"react": "^19.2.7",
"react-dom": "^19.2.7"
}
}
87 changes: 86 additions & 1 deletion tests/ChipPartitionsSolver.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from "bun:test"
import { expect, test } from "bun:test"
import { ChipPartitionsSolver } from "../lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver"
import type { InputProblem } from "../lib/types/InputProblem"

Expand Down Expand Up @@ -162,6 +162,91 @@ test("ChipPartitionsSolver handles complex connected graph", () => {
expect(solver.partitions[0]!.chipMap).toEqual(inputProblem.chipMap)
})

test("ChipPartitionsSolver separates small passive support components from large chips", () => {
const inputProblem: InputProblem = {
chipMap: {
U1: {
chipId: "U1",
pins: ["U1.1", "U1.2", "U1.3", "U1.4"],
size: { x: 4, y: 4 },
},
R1: {
chipId: "R1",
pins: ["R1.1", "R1.2"],
size: { x: 1, y: 0.4 },
},
},
chipPinMap: {
"U1.1": { pinId: "U1.1", offset: { x: -2, y: 1 }, side: "x-" },
"U1.2": { pinId: "U1.2", offset: { x: -2, y: 0 }, side: "x-" },
"U1.3": { pinId: "U1.3", offset: { x: 2, y: 0 }, side: "x+" },
"U1.4": { pinId: "U1.4", offset: { x: 2, y: 1 }, side: "x+" },
"R1.1": { pinId: "R1.1", offset: { x: -0.5, y: 0 }, side: "x-" },
"R1.2": { pinId: "R1.2", offset: { x: 0.5, y: 0 }, side: "x+" },
},
netMap: {},
pinStrongConnMap: {
"U1.1-R1.1": true,
"R1.1-U1.1": true,
},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
}

const solver = new ChipPartitionsSolver({ inputProblem })
solver.solve()

const partitions = solver.partitions.map((partition) =>
Object.keys(partition.chipMap),
)
expect(partitions).toHaveLength(2)
expect(partitions.flat().sort()).toEqual(["R1", "U1"])
})

test("ChipPartitionsSolver keeps small active components attached to large chips", () => {
const inputProblem: InputProblem = {
chipMap: {
U1: {
chipId: "U1",
pins: ["U1.1", "U1.2", "U1.3", "U1.4"],
size: { x: 4, y: 4 },
},
Q1: {
chipId: "Q1",
pins: ["Q1.1", "Q1.2", "Q1.3"],
size: { x: 1, y: 1 },
},
},
chipPinMap: {
"U1.1": { pinId: "U1.1", offset: { x: -2, y: 1 }, side: "x-" },
"U1.2": { pinId: "U1.2", offset: { x: -2, y: 0 }, side: "x-" },
"U1.3": { pinId: "U1.3", offset: { x: 2, y: 0 }, side: "x+" },
"U1.4": { pinId: "U1.4", offset: { x: 2, y: 1 }, side: "x+" },
"Q1.1": { pinId: "Q1.1", offset: { x: -0.5, y: 0 }, side: "x-" },
"Q1.2": { pinId: "Q1.2", offset: { x: 0.5, y: 0 }, side: "x+" },
"Q1.3": { pinId: "Q1.3", offset: { x: 0, y: 0.5 }, side: "y+" },
},
netMap: {},
pinStrongConnMap: {
"U1.1-Q1.1": true,
"Q1.1-U1.1": true,
},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
}

const solver = new ChipPartitionsSolver({ inputProblem })
solver.solve()

expect(solver.partitions).toHaveLength(1)
expect(Object.keys(solver.partitions[0]!.chipMap).sort()).toEqual([
"Q1",
"U1",
])
})

test("ChipPartitionsSolver visualization contains partition components", () => {
const inputProblem: InputProblem = {
chipMap: {
Expand Down
90 changes: 88 additions & 2 deletions tests/PartitionPackingSolver/PartitionPackingSolver01.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { test, expect } from "bun:test"
import { expect, test } from "bun:test"
import type { PackedPartition } from "../../lib/solvers/PackInnerPartitionsSolver/PackInnerPartitionsSolver"
import {
PartitionPackingSolver,
type PartitionPackingSolverInput,
} from "../../lib/solvers/PartitionPackingSolver/PartitionPackingSolver"
import type { PackedPartition } from "../../lib/solvers/PackInnerPartitionsSolver/PackInnerPartitionsSolver"
import type { InputProblem } from "../../lib/types/InputProblem"

test("PartitionPackingSolver works with single packed partition", () => {
// Create a simple packed partition
Expand Down Expand Up @@ -135,3 +136,88 @@ test("PartitionPackingSolver works with empty partitions", () => {
expect(solver.finalLayout).toBeDefined()
expect(Object.keys(solver.finalLayout!.chipPlacements).length).toBe(0)
})

test("PartitionPackingSolver keeps original cross-partition strong connections", () => {
const inputProblem: InputProblem = {
chipMap: {
U1: {
chipId: "U1",
pins: ["U1.1", "U1.2", "U1.3", "U1.4"],
size: { x: 4, y: 4 },
},
R1: {
chipId: "R1",
pins: ["R1.1", "R1.2"],
size: { x: 1, y: 0.4 },
},
},
chipPinMap: {
"U1.1": { pinId: "U1.1", offset: { x: -2, y: 1 }, side: "x-" },
"U1.2": { pinId: "U1.2", offset: { x: -2, y: 0 }, side: "x-" },
"U1.3": { pinId: "U1.3", offset: { x: 2, y: 0 }, side: "x+" },
"U1.4": { pinId: "U1.4", offset: { x: 2, y: 1 }, side: "x+" },
"R1.1": { pinId: "R1.1", offset: { x: -0.5, y: 0 }, side: "x-" },
"R1.2": { pinId: "R1.2", offset: { x: 0.5, y: 0 }, side: "x+" },
},
netMap: {},
pinStrongConnMap: {
"U1.1-R1.1": true,
"R1.1-U1.1": true,
},
netConnMap: {},
chipGap: 0.2,
partitionGap: 2,
}
const packedPartitions: PackedPartition[] = [
{
inputProblem: {
...inputProblem,
chipMap: { U1: inputProblem.chipMap.U1! },
chipPinMap: {
"U1.1": inputProblem.chipPinMap["U1.1"]!,
"U1.2": inputProblem.chipPinMap["U1.2"]!,
"U1.3": inputProblem.chipPinMap["U1.3"]!,
"U1.4": inputProblem.chipPinMap["U1.4"]!,
},
pinStrongConnMap: {},
netConnMap: {},
},
layout: {
chipPlacements: {
U1: { x: 0, y: 0, ccwRotationDegrees: 0 },
},
groupPlacements: {},
},
},
{
inputProblem: {
...inputProblem,
chipMap: { R1: inputProblem.chipMap.R1! },
chipPinMap: {
"R1.1": inputProblem.chipPinMap["R1.1"]!,
"R1.2": inputProblem.chipPinMap["R1.2"]!,
},
pinStrongConnMap: {},
netConnMap: {},
},
layout: {
chipPlacements: {
R1: { x: 0, y: 0, ccwRotationDegrees: 0 },
},
groupPlacements: {},
},
},
]

const solver = new PartitionPackingSolver({
packedPartitions,
inputProblem,
})
const connectivityMap = (solver as any).buildConnectivityMap() as Map<
string,
string
>

expect(connectivityMap.get("U1.1")).toBe("U1.1-R1.1")
expect(connectivityMap.get("R1.1")).toBe("U1.1-R1.1")
})
Loading
Loading