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
4 changes: 4 additions & 0 deletions lib/solvers/LayoutPipelineSolver/LayoutPipelineSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
100 changes: 100 additions & 0 deletions lib/solvers/LayoutPipelineSolver/applyVoltageBias.ts
Original file line number Diff line number Diff line change
@@ -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<string>()
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<string>()
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
}
2 changes: 2 additions & 0 deletions lib/types/InputProblem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
45 changes: 45 additions & 0 deletions reproduce_bad_layout.ts
Original file line number Diff line number Diff line change
@@ -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}°`)
})
75 changes: 75 additions & 0 deletions test_voltage_bias.ts
Original file line number Diff line number Diff line change
@@ -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})`)
}
}
}
}
}