diff --git a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts index 88db103..aa11275 100644 --- a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts +++ b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts @@ -16,12 +16,21 @@ 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 @@ -65,7 +74,7 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } private createPackInput(): PackInput { - // Fall back to filtered mapping (weak + strong) + // Use filtered mapping (weak + strong) const pinToNetworkMap = createFilteredNetworkMapping({ inputProblem: this.partitionInputProblem, pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, @@ -75,7 +84,6 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { 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 @@ -89,15 +97,22 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { 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` + // Apply upward bias for positive voltage nets + 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 +122,7 @@ 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) - + // Add chip body pad (disconnected) that envelopes all 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..63115c2 100644 --- a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts +++ b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts @@ -10,12 +10,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 +38,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 +47,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 +69,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 +93,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 +111,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 @@ -128,7 +127,6 @@ export class PartitionPackingSolver extends BaseSolver { placement.ccwRotationDegrees === 90 || placement.ccwRotationDegrees === 270 ) { - // Swap width and height for 90/270 degree rotations ;[chipWidth, chipHeight] = [chipHeight, chipWidth] } @@ -143,12 +141,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 +164,10 @@ export class PartitionPackingSolver extends BaseSolver { } }>, ): PackInput { - // Build a global connectivity map to properly assign networkIds + // Build a global connectivity map 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 +178,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) } @@ -210,13 +201,11 @@ export class PartitionPackingSolver extends BaseSolver { 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 +219,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]! @@ -255,31 +242,34 @@ 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 + // Apply upward bias for positive voltage nets + 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.01, y: 0.01 }, }) } } @@ -288,15 +278,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 Array<0 | 90 | 180 | 270>, } }) 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,7 +303,6 @@ export class PartitionPackingSolver extends BaseSolver { } }>, ): OutputLayout { - // Apply the partition offsets to individual components const newChipPlacements: Record = {} for (const packedComponent of packedComponents) { @@ -326,7 +315,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 +323,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]! @@ -374,7 +361,6 @@ export class PartitionPackingSolver extends BaseSolver { partitionGap: this.inputProblem.partitionGap, } - // Combine all packed partitions for (const packedPartition of this.packedPartitions) { Object.assign( combinedProblem.chipMap, @@ -405,3 +391,4 @@ export class PartitionPackingSolver extends BaseSolver { } } } + diff --git a/lib/utils/networkFiltering.ts b/lib/utils/networkFiltering.ts index df3c7c6..914905f 100644 --- a/lib/utils/networkFiltering.ts +++ b/lib/utils/networkFiltering.ts @@ -44,12 +44,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}` @@ -69,20 +67,18 @@ 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 + // Filter out all weak (pin-to-net) connections when strong connections are present 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 + // No strong connections; include weak connections with opposite-side filtering for (const [connKey, connected] of Object.entries( inputProblem.netConnMap, )) { @@ -95,7 +91,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 +98,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 +110,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 +124,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) @@ -149,14 +139,12 @@ export function createFilteredNetworkMapping(params: { 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 +156,17 @@ 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 +} +