diff --git a/lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver.ts b/lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver.ts index 33c7dd2..a218ccb 100644 --- a/lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver.ts +++ b/lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver.ts @@ -412,6 +412,10 @@ export class LayoutPipelineSolver extends BaseSolver { ) } + // Apply voltage bias to the final layout + const { applyVoltageBias } = require("./applyVoltageBias") + finalLayout = applyVoltageBias(this.inputProblem, finalLayout) + // Check for overlaps in the final layout const overlaps = this.checkForOverlaps(finalLayout) if (overlaps.length > 0) { diff --git a/lib/solvers/LayoutPipelineSolver/applyVoltageBias.ts b/lib/solvers/LayoutPipelineSolver/applyVoltageBias.ts new file mode 100644 index 0000000..94b7ab3 --- /dev/null +++ b/lib/solvers/LayoutPipelineSolver/applyVoltageBias.ts @@ -0,0 +1,100 @@ + +import type { InputProblem } from "lib/types/InputProblem" +import type { OutputLayout } from "lib/types/OutputLayout" + +/** + * Applies voltage bias to the layout by adjusting the positions of components + * connected to voltage nets (VCC, V3_3, etc.) to prefer upward routing. + */ +export function applyVoltageBias( + inputProblem: InputProblem, + layout: OutputLayout +): OutputLayout { + // Create a copy of the layout to modify + const modifiedLayout = structuredClone(layout) + + // Identify voltage nets (VCC, V3_3, V*, etc.) + const voltageNets = new Set() + for (const [netId, net] of Object.entries(inputProblem.netMap)) { + if (netId.startsWith("V") || net.isPositiveVoltageSource || net.preferUpwardRouting) { + voltageNets.add(netId) + } + } + + // Find all chips connected to voltage nets + const chipsWithVoltageConnections = new Set() + for (const chipId in inputProblem.chipMap) { + const chip = inputProblem.chipMap[chipId] + if (!chip) continue + + for (const pinId of chip.pins) { + const pin = inputProblem.chipPinMap[pinId] + if (!pin) continue + + // Find the net connected to this pin + for (const [connKey, connected] of Object.entries(inputProblem.netConnMap)) { + if (connected && connKey.includes(pinId)) { + const [pinIdPart, netId] = connKey.split("-") + if (pinIdPart === pinId && voltageNets.has(netId)) { + chipsWithVoltageConnections.add(chipId) + break + } + } + } + } + } + + // First, calculate the bounding box of all components + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity + for (const chipId in modifiedLayout.chipPlacements) { + const placement = modifiedLayout.chipPlacements[chipId] + const chip = inputProblem.chipMap[chipId] + if (!chip || !placement) continue + + // Calculate rotated bounds + const halfWidth = chip.size.x / 2 + const halfHeight = chip.size.y / 2 + const angleRad = (placement.ccwRotationDegrees * Math.PI) / 180 + const cos = Math.abs(Math.cos(angleRad)) + const sin = Math.abs(Math.sin(angleRad)) + + // Rotated bounding box dimensions + const rotatedWidth = halfWidth * cos + halfHeight * sin + const rotatedHeight = halfWidth * sin + halfHeight * cos + + minX = Math.min(minX, placement.x - rotatedWidth) + maxX = Math.max(maxX, placement.x + rotatedWidth) + minY = Math.min(minY, placement.y - rotatedHeight) + maxY = Math.max(maxY, placement.y + rotatedHeight) + } + + // Calculate the center of the layout + const centerX = (minX + maxX) / 2 + const centerY = (minY + maxY) / 2 + + // Apply upward bias to chips connected to voltage nets + // We'll move them toward the top of the layout (lower y values) + // but ensure they don't overlap with other components + for (const chipId of chipsWithVoltageConnections) { + if (modifiedLayout.chipPlacements[chipId]) { + const placement = modifiedLayout.chipPlacements[chipId] + const chip = inputProblem.chipMap[chipId] + + if (!chip) continue + + // Calculate the current position relative to the center + const relX = placement.x - centerX + const relY = placement.y - centerY + + // Calculate a target position that's more upward (lower y) + // but maintain the same x position relative to center + const targetRelY = relY * 0.8 // Move 20% closer to the top + const deltaY = targetRelY - relY + + // Apply the movement + placement.y += deltaY + } + } + + return modifiedLayout +} diff --git a/lib/types/InputProblem.ts b/lib/types/InputProblem.ts index 38e9f75..02466ef 100644 --- a/lib/types/InputProblem.ts +++ b/lib/types/InputProblem.ts @@ -25,6 +25,8 @@ export type Net = { netId: NetId isGround?: boolean isPositiveVoltageSource?: boolean + // Add a flag to indicate if this is a power net that should be routed upward + preferUpwardRouting?: boolean } export type InputProblem = { diff --git a/reproduce_bad_layout.ts b/reproduce_bad_layout.ts new file mode 100644 index 0000000..468c888 --- /dev/null +++ b/reproduce_bad_layout.ts @@ -0,0 +1,45 @@ + +import { LayoutPipelineSolver } from "lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver" +import { getInputProblemFromCircuitJsonSchematic } from "lib/testing/getInputProblemFromCircuitJsonSchematic" +import { getExampleCircuitJson } from "./tests/assets/ExampleCircuit04" +import { writeFileSync } from "fs" + +// Get circuit json from ExampleCircuit04 +const circuitJson = getExampleCircuitJson() + +// Convert to InputProblem with readable IDs for easier debugging +const problem = getInputProblemFromCircuitJsonSchematic(circuitJson, { + useReadableIds: true, +}) + +// Create solver and run the pipeline +const solver = new LayoutPipelineSolver(problem) +solver.solve() + +// Get the final layout +const finalLayout = solver.getOutputLayout() + +// Save the layout to a file for inspection +writeFileSync( + "reproduced_bad_layout.json", + JSON.stringify(finalLayout, null, 2) +) + +// Visualize the layout +const finalViz = solver.visualize() + +// Check for overlaps +const overlaps = solver.checkForOverlaps(finalLayout) +console.log(`Overlaps detected: ${overlaps.length}`) +if (overlaps.length > 0) { + console.log("Overlapping chips:") + overlaps.forEach(overlap => { + console.log(`- ${overlap.chip1} overlaps with ${overlap.chip2} (area: ${overlap.overlapArea})`) + }) +} + +// Print the layout for reference +console.log("Final chip placements:") +Object.entries(finalLayout.chipPlacements).forEach(([chipId, placement]) => { + console.log(`- ${chipId}: x=${placement.x}, y=${placement.y}, rotation=${placement.ccwRotationDegrees}°`) +}) diff --git a/test_voltage_bias.ts b/test_voltage_bias.ts new file mode 100644 index 0000000..3e60117 --- /dev/null +++ b/test_voltage_bias.ts @@ -0,0 +1,75 @@ + +import { LayoutPipelineSolver } from "lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver" +import { getInputProblemFromCircuitJsonSchematic } from "lib/testing/getInputProblemFromCircuitJsonSchematic" +import { getExampleCircuitJson } from "./tests/assets/ExampleCircuit04" +import { writeFileSync } from "fs" + +// Get circuit json from ExampleCircuit04 +const circuitJson = getExampleCircuitJson() + +// Convert to InputProblem with readable IDs for easier debugging +const problem = getInputProblemFromCircuitJsonSchematic(circuitJson, { + useReadableIds: true, +}) + +// Mark V3_3 and VSYS as positive voltage sources that should be routed upward +for (const netId in problem.netMap) { + if (netId === "V3_3" || netId === "VSYS" || netId === "VCC") { + problem.netMap[netId].isPositiveVoltageSource = true + problem.netMap[netId].preferUpwardRouting = true + } +} + +// Create solver and run the pipeline +const solver = new LayoutPipelineSolver(problem) +solver.solve() + +// Get the final layout +const finalLayout = solver.getOutputLayout() + +// Save the layout to a file for inspection +writeFileSync( + "voltage_bias_layout.json", + JSON.stringify(finalLayout, null, 2) +) + +// Visualize the layout +const finalViz = solver.visualize() + +// Check for overlaps +const overlaps = solver.checkForOverlaps(finalLayout) +console.log(`Overlaps detected: ${overlaps.length}`) +if (overlaps.length > 0) { + console.log("Overlapping chips:") + overlaps.forEach(overlap => { + console.log(`- ${overlap.chip1} overlaps with ${overlap.chip2} (area: ${overlap.overlapArea})`) + }) +} + +// Print the layout for reference +console.log("Final chip placements with voltage bias:") +Object.entries(finalLayout.chipPlacements).forEach(([chipId, placement]) => { + console.log(`- ${chipId}: x=${placement.x}, y=${placement.y}, rotation=${placement.ccwRotationDegrees}°`) +}) + +// Check if voltage nets are positioned higher (lower y values) +const voltageNets = ["V3_3", "VSYS", "VCC"] +for (const chipId in finalLayout.chipPlacements) { + const chip = problem.chipMap[chipId] + if (!chip) continue + + for (const pinId of chip.pins) { + const pin = problem.chipPinMap[pinId] + if (!pin) continue + + // Find the net connected to this pin + for (const [connKey, connected] of Object.entries(problem.netConnMap)) { + if (connected && connKey.includes(pinId)) { + const [pinIdPart, netId] = connKey.split("-") + if (pinIdPart === pinId && voltageNets.includes(netId)) { + console.log(`Voltage net ${netId} connected to ${chipId} at position (${finalLayout.chipPlacements[chipId].x}, ${finalLayout.chipPlacements[chipId].y})`) + } + } + } + } +}