diff --git a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts index 88db103..9bbb8ba 100644 --- a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts +++ b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts @@ -1,8 +1,3 @@ -/** - * Packs components within a single partition to create an optimal internal layout. - * Uses a packing algorithm to arrange chips and their connections within the partition. - */ - import type { GraphicsObject } from "graphics-debug" import { type PackInput, PackSolver2 } from "calculate-packing" import { BaseSolver } from "../BaseSolver" @@ -16,12 +11,18 @@ import type { PartitionInputProblem, } from "../../types/InputProblem" import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem" -import { createFilteredNetworkMapping } from "../../utils/networkFiltering" +import { createFilteredNetworkMapping, isPositiveVoltageNet } from "../../utils/networkFiltering" import { getPadsBoundingBox } from "./getPadsBoundingBox" import { doBasicInputProblemLayout } from "../LayoutPipelineSolver/doBasicInputProblemLayout" const PIN_SIZE = 0.1 +/** + * Y-axis bias applied to pins connected to positive voltage nets. + * Negative value pushes them upward in the schematic layout. + */ +const POSITIVE_VOLTAGE_Y_BIAS = -0.3 + export class SingleInnerPartitionPackingSolver extends BaseSolver { partitionInputProblem: PartitionInputProblem layout: OutputLayout | null = null @@ -38,14 +39,12 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } override _step() { - // Initialize PackSolver2 if not already created if (!this.activeSubSolver) { const packInput = this.createPackInput() this.activeSubSolver = new PackSolver2(packInput) this.activeSubSolver = this.activeSubSolver } - // Run one step of the PackSolver2 this.activeSubSolver.step() if (this.activeSubSolver.failed) { @@ -55,7 +54,6 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } if (this.activeSubSolver.solved) { - // Apply the packing result to create the layout this.layout = this.createLayoutFromPackingResult( this.activeSubSolver.packedComponents, ) @@ -65,17 +63,14 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } private createPackInput(): PackInput { - // Fall back to filtered mapping (weak + strong) const pinToNetworkMap = createFilteredNetworkMapping({ inputProblem: this.partitionInputProblem, pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, }).pinToNetworkMap - // Create pack components for each chip const packComponents = Object.entries( this.partitionInputProblem.chipMap, ).map(([chipId, chip]) => { - // Create pads for all pins of this chip const pads: Array<{ padId: string networkId: string @@ -84,20 +79,22 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { size: { x: number; y: number } }> = [] - // Create a pad for each pin on this chip for (const pinId of chip.pins) { const pin = this.partitionInputProblem.chipPinMap[pinId] if (!pin) continue - // Find network for this pin from our connectivity map const networkId = pinToNetworkMap.get(pinId) || `${pinId}_isolated` + const voltageBias = isPositiveVoltageNet(networkId, this.partitionInputProblem) + ? POSITIVE_VOLTAGE_Y_BIAS + : 0 + pads.push({ padId: pinId, networkId: networkId, type: "rect" as const, - offset: { x: pin.offset.x, y: pin.offset.y }, - size: { x: PIN_SIZE, y: PIN_SIZE }, // Small size for pins + offset: { x: pin.offset.x, y: pin.offset.y + voltageBias }, + size: { x: PIN_SIZE, y: PIN_SIZE }, }) } @@ -107,9 +104,6 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { y: padsBoundingBox.maxY - padsBoundingBox.minY, } - // Add chip body pad (disconnected from any network) but make sure - // it fully envelopes the "pads" (pins) - pads.push({ padId: `${chipId}_body`, networkId: `${chipId}_body_disconnected`, diff --git a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts index 149d617..9dcd27e 100644 --- a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts +++ b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts @@ -1,8 +1,3 @@ -/** - * Packs the laid out chip partitions into a single layout. - * 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" @@ -10,12 +5,19 @@ import type { OutputLayout, Placement } from "../../types/OutputLayout" import type { InputProblem } from "../../types/InputProblem" import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem" import type { PackedPartition } from "../PackInnerPartitionsSolver/PackInnerPartitionsSolver" +import { isPositiveVoltageNet } from "../../utils/networkFiltering" export interface PartitionPackingSolverInput { packedPartitions: PackedPartition[] inputProblem: InputProblem } +/** + * Y-axis bias applied to partition-level pads connected to positive voltage nets. + * Negative value pushes them upward in the final schematic layout. + */ +const POSITIVE_VOLTAGE_Y_BIAS = -0.5 + export class PartitionPackingSolver extends BaseSolver { packedPartitions: PackedPartition[] inputProblem: InputProblem @@ -31,7 +33,6 @@ export class PartitionPackingSolver extends BaseSolver { override _step() { try { if (this.packedPartitions.length === 0) { - // No partitions to pack, create empty layout this.finalLayout = { chipPlacements: {}, groupPlacements: {}, @@ -41,23 +42,19 @@ export class PartitionPackingSolver extends BaseSolver { } if (this.packedPartitions.length === 1) { - // Only one partition, use its layout directly this.finalLayout = this.packedPartitions[0]!.layout this.solved = true return } - // Create groups of components by partition for better organization const partitionGroups = this.organizePackedPartitions() - // Initialize PackSolver2 if not already created if (!this.packSolver2) { const packInput = this.createPackInput(partitionGroups) this.packSolver2 = new PackSolver2(packInput) this.activeSubSolver = this.packSolver2 } - // Run one step of the PackSolver2 this.packSolver2.step() if (this.packSolver2.failed) { @@ -67,7 +64,6 @@ export class PartitionPackingSolver extends BaseSolver { } if (this.packSolver2.solved) { - // Apply the packing result to the layout const packedLayout = this.applyPackingResult( this.packSolver2.packedComponents, partitionGroups, @@ -92,7 +88,6 @@ export class PartitionPackingSolver extends BaseSolver { maxY: number } }> { - // Group chips by partition based on packed partitions const partitionGroups: Array<{ partitionIndex: number chipIds: string[] @@ -111,7 +106,6 @@ export class PartitionPackingSolver extends BaseSolver { ) if (partitionChipIds.length > 0) { - // Calculate bounding box for this partition including chip sizes let minX = Infinity let maxX = -Infinity let minY = Infinity @@ -121,14 +115,12 @@ export class PartitionPackingSolver extends BaseSolver { const placement = packedPartition.layout.chipPlacements[chipId]! const chip = packedPartition.inputProblem.chipMap[chipId]! - // Account for chip size and rotation let chipWidth = chip.size.x let chipHeight = chip.size.y if ( placement.ccwRotationDegrees === 90 || placement.ccwRotationDegrees === 270 ) { - // Swap width and height for 90/270 degree rotations ;[chipWidth, chipHeight] = [chipHeight, chipWidth] } @@ -143,12 +135,10 @@ export class PartitionPackingSolver extends BaseSolver { maxY = Math.max(maxY, chipMaxY) } - const bounds = { minX, maxX, minY, maxY } - partitionGroups.push({ partitionIndex: i, chipIds: partitionChipIds, - bounds, + bounds: { minX, maxX, minY, maxY }, }) } } @@ -168,12 +158,9 @@ export class PartitionPackingSolver extends BaseSolver { } }>, ): PackInput { - // Build a global connectivity map to properly assign networkIds const pinToNetworkMap = new Map() - // First, process all partitions to build the connectivity map for (const packedPartition of this.packedPartitions) { - // Process net connections for (const [connKey, connected] of Object.entries( packedPartition.inputProblem.netConnMap, )) { @@ -184,21 +171,18 @@ export class PartitionPackingSolver extends BaseSolver { } } - // Process strong connections - these form their own networks 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]) { - // If either pin already has a net connection, use that network for both const existingNet = pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1]) if (existingNet) { pinToNetworkMap.set(pins[0], existingNet) pinToNetworkMap.set(pins[1], existingNet) } else { - // Otherwise, use the connection itself as the network pinToNetworkMap.set(pins[0], connKey) pinToNetworkMap.set(pins[1], connKey) } @@ -206,17 +190,14 @@ export class PartitionPackingSolver extends BaseSolver { } } - // Create pack components for each partition group const packComponents = partitionGroups.map((group) => { const packedPartition = this.packedPartitions[group.partitionIndex]! - // Calculate partition size from bounds const partitionWidth = group.bounds.maxX - group.bounds.minX const partitionHeight = group.bounds.maxY - group.bounds.minY const centerX = (group.bounds.minX + group.bounds.maxX) / 2 const centerY = (group.bounds.minY + group.bounds.maxY) / 2 - // Start with the partition body pad const pads = [ { padId: `partition_${group.partitionIndex}_body`, @@ -230,11 +211,9 @@ export class PartitionPackingSolver extends BaseSolver { }, ] - // Add all pins from this partition as pads const addedNetworks = new Set() const pinPositions = new Map() - // Calculate pin positions for all chips in the partition for (const chipId of group.chipIds) { const chipPlacement = packedPartition.layout.chipPlacements[chipId]! const chip = packedPartition.inputProblem.chipMap[chipId]! @@ -243,7 +222,6 @@ export class PartitionPackingSolver extends BaseSolver { const chipPin = packedPartition.inputProblem.chipPinMap[pinId] if (!chipPin) continue - // Transform pin offset based on chip rotation let transformedOffset = { x: chipPin.offset.x, y: chipPin.offset.y } const rotation = chipPlacement.ccwRotationDegrees || 0 @@ -255,31 +233,30 @@ export class PartitionPackingSolver extends BaseSolver { transformedOffset = { x: chipPin.offset.y, y: -chipPin.offset.x } } - // Calculate absolute pin position const absolutePinX = chipPlacement.x + transformedOffset.x const absolutePinY = chipPlacement.y + transformedOffset.y - // Store pin position for use in pad offset calculation pinPositions.set(pinId, { x: absolutePinX, y: absolutePinY }) - // Get the network ID for this pin const networkId = pinToNetworkMap.get(pinId) || `${pinId}_disconnected` - // Only add one pad per network to avoid overlapping if (!addedNetworks.has(networkId)) { addedNetworks.add(networkId) - // Calculate offset relative to partition center const padOffsetX = absolutePinX - centerX const padOffsetY = absolutePinY - centerY + const voltageBias = isPositiveVoltageNet(networkId, this.inputProblem) + ? POSITIVE_VOLTAGE_Y_BIAS + : 0 + pads.push({ padId: `${group.partitionIndex}_pin_${pinId}`, networkId: networkId, type: "rect" as const, - offset: { x: padOffsetX, y: padOffsetY }, - size: { x: 0.01, y: 0.01 }, // Small pin pad + offset: { x: padOffsetX, y: padOffsetY + voltageBias }, + size: { x: 0.05, y: 0.05 }, }) } } @@ -288,15 +265,15 @@ export class PartitionPackingSolver extends BaseSolver { return { componentId: `partition_${group.partitionIndex}`, pads, - availableRotationDegrees: [0] as Array<0 | 90 | 180 | 270>, // Keep partitions unrotated + availableRotationDegrees: [0 as const], } }) return { components: packComponents, - minGap: this.inputProblem.partitionGap, // Use partitionGap from input problem + minGap: this.inputProblem.partitionGap, packOrderStrategy: "largest_to_smallest", - packPlacementStrategy: "minimum_sum_squared_distance_to_network", + packPlacementStrategy: "minimum_closest_sum_squared_distance", } } @@ -313,11 +290,10 @@ export class PartitionPackingSolver extends BaseSolver { } }>, ): OutputLayout { - // Apply the partition offsets to individual components const newChipPlacements: Record = {} for (const packedComponent of packedComponents) { - const partitionIndex = parseInt( + const partitionIndex = Number.parseInt( packedComponent.componentId.replace("partition_", ""), ) const group = partitionGroups.find( @@ -326,7 +302,6 @@ export class PartitionPackingSolver extends BaseSolver { const packedPartition = this.packedPartitions[partitionIndex] if (group && packedPartition) { - // Calculate offset to apply to this partition's components const currentCenterX = (group.bounds.minX + group.bounds.maxX) / 2 const currentCenterY = (group.bounds.minY + group.bounds.maxY) / 2 const newCenterX = packedComponent.center.x @@ -335,7 +310,6 @@ export class PartitionPackingSolver extends BaseSolver { const offsetX = newCenterX - currentCenterX const offsetY = newCenterY - currentCenterY - // Apply offset to all chips in this partition for (const chipId of group.chipIds) { const originalPlacement = packedPartition.layout.chipPlacements[chipId]! @@ -363,7 +337,6 @@ export class PartitionPackingSolver extends BaseSolver { return super.visualize() } - // Create a combined problem for visualization from all packed partitions const combinedProblem: InputProblem = { chipMap: {}, chipPinMap: {}, @@ -374,7 +347,6 @@ export class PartitionPackingSolver extends BaseSolver { partitionGap: this.inputProblem.partitionGap, } - // Combine all packed partitions for (const packedPartition of this.packedPartitions) { Object.assign( combinedProblem.chipMap, diff --git a/lib/utils/networkFiltering.ts b/lib/utils/networkFiltering.ts index df3c7c6..3aa3671 100644 --- a/lib/utils/networkFiltering.ts +++ b/lib/utils/networkFiltering.ts @@ -8,9 +8,7 @@ import type { ChipPin, InputProblem, PinId } from "../types/InputProblem" export interface NetworkFilteringResult { - /** Map from pinId to networkId, with filtered networks marked as disconnected */ pinToNetworkMap: Map - /** Set of pins whose weak connections were filtered (disconnected) */ filteredPins: Set } @@ -30,7 +28,6 @@ export function createFilteredNetworkMapping(params: { const filteredPins = new Set() let hasStrongConnections = false - // First, collect all strong connections to identify chip-to-chip relationships const strongConnectedChipSides = new Map>() for (const [connKey, connected] of Object.entries( @@ -44,12 +41,10 @@ export function createFilteredNetworkMapping(params: { const pin2 = inputProblem.chipPinMap[pins[1]] if (pin1 && pin2) { - // Extract chip IDs const chip1Id = pins[0].split(".")[0] const chip2Id = pins[1].split(".")[0] if (chip1Id && chip2Id && chip1Id !== chip2Id) { - // Track which sides of each chip are strongly connected const key1 = `${chip1Id}-${chip2Id}` const key2 = `${chip2Id}-${chip1Id}` @@ -67,22 +62,17 @@ export function createFilteredNetworkMapping(params: { } } - // Process net connections if (hasStrongConnections) { - // If any strong connections exist anywhere in the problem, filter out all weak (pin-to-net) connections for (const [connKey, connected] of Object.entries( inputProblem.netConnMap, )) { if (!connected) continue const [pinId, netId] = connKey.split("-") if (pinId && netId) { - // Do not assign a network for weak connections when strong connections are present. - // Mark this pin as filtered so callers can inspect what was ignored. filteredPins.add(pinId) } } } else { - // No strong connections exist; include weak connections with basic opposite-side filtering for (const [connKey, connected] of Object.entries( inputProblem.netConnMap, )) { @@ -95,7 +85,6 @@ export function createFilteredNetworkMapping(params: { const chipId = pinId.split(".")[0] let shouldIncludeInNetwork = true - // Check if this pin is on the opposite side of a chip we have strong connections to for (const [ strongKey, strongSides, @@ -103,8 +92,6 @@ export function createFilteredNetworkMapping(params: { const [fromChip, toChip] = strongKey.split("-") if (fromChip === chipId) { - // This pin belongs to a chip that has strong connections - // Check if any pins in this net belong to chips on the opposite side for (const [otherConnKey, otherConnected] of Object.entries( inputProblem.netConnMap, )) { @@ -117,9 +104,7 @@ export function createFilteredNetworkMapping(params: { const otherChipId = otherPinId.split(".")[0] - // If this net connects to a chip we have strong connections with if (otherChipId === toChip) { - // Check if the other pin is on a different side than our strong connections if (!strongSides.has(otherPin.side)) { shouldIncludeInNetwork = false break @@ -133,7 +118,6 @@ export function createFilteredNetworkMapping(params: { if (shouldIncludeInNetwork) { pinToNetworkMap.set(pinId, netId) } else { - // Mark as opposite-strong-side-disconnected const disconnectedNetworkId = `${pinId}_opposite-strong-side-disconnected` pinToNetworkMap.set(pinId, disconnectedNetworkId) filteredPins.add(pinId) @@ -142,21 +126,18 @@ export function createFilteredNetworkMapping(params: { } } - // Process strong connections - these form their own networks for (const [connKey, connected] of Object.entries( inputProblem.pinStrongConnMap, )) { if (!connected) continue const pins = connKey.split("-") if (pins.length === 2 && pins[0] && pins[1]) { - // If either pin already has a net connection, use that network for both const existingNet = pinToNetworkMap.get(pins[0]) || pinToNetworkMap.get(pins[1]) if (existingNet) { pinToNetworkMap.set(pins[0], existingNet) pinToNetworkMap.set(pins[1], existingNet) } else { - // Otherwise, use the connection itself as the network pinToNetworkMap.set(pins[0], connKey) pinToNetworkMap.set(pins[1], connKey) } @@ -168,3 +149,16 @@ export function createFilteredNetworkMapping(params: { filteredPins, } } + +/** + * Checks if a given net is a positive voltage source net (e.g., VCC, V+, VDD). + * Uses the `isPositiveVoltageSource` flag from the net definition in the input problem. + */ +export function isPositiveVoltageNet( + netId: string, + inputProblem: InputProblem, +): boolean { + const net = inputProblem.netMap[netId] + if (!net) return false + return net.isPositiveVoltageSource === true +}