Skip to content

Commit

Permalink
Merge pull request #28 from tscircuit/samenet
Browse files Browse the repository at this point in the history
Autorouter same-net detection improvements, example1 fully routing
  • Loading branch information
seveibar authored Sep 4, 2024
2 parents 1249df2 + e58a627 commit 3462fda
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 64 deletions.
13 changes: 2 additions & 11 deletions lib/components/base-components/NormalComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { getPortFromHints } from "lib/utils/getPortFromHints"
import { createComponentsFromSoup } from "lib/utils/createComponentsFromSoup"
import { Net } from "../primitive-components/Net"
import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"

export type PortMap<T extends string> = {
[K in T]: Port
Expand Down Expand Up @@ -309,17 +310,7 @@ export class NormalComponent<
}

_createNetsFromProps(propsWithConnections: (string | undefined | null)[]) {
for (const prop of propsWithConnections) {
if (typeof prop === "string" && prop.startsWith("net.")) {
if (!this.getSubcircuit().selectOne(prop)) {
this.getSubcircuit().add(
new Net({
name: prop.split(".")[1],
}),
)
}
}
}
createNetsFromProps(this, propsWithConnections)
}

/**
Expand Down
131 changes: 81 additions & 50 deletions lib/components/primitive-components/Trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ import type {
import { computeObstacleBounds } from "lib/utils/autorouting/computeObstacleBounds"
import { projectPointInDirection } from "lib/utils/projectPointInDirection"
import type { TraceHint } from "./TraceHint"
import { findPossibleTraceLayerCombinations } from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
import {
findPossibleTraceLayerCombinations,
type CandidateTraceLayerCombination,
} from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
import { pairs } from "lib/utils/pairs"
import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"
import type { Net } from "./Net"
import { getClosest } from "lib/utils/getClosest"
import { z } from "zod"
import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"

type PcbRouteObjective =
| RouteHintPoint
Expand Down Expand Up @@ -144,20 +148,47 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
}
}

_findConnectedNets(): Array<{ selector: string; net: Net }> {
const nets = this.getTracePathNetSelectors().map((selector) => ({
selector,
net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
}))
_findConnectedNets(): {
nets: Net[]
netsWithSelectors: Array<{ selector: string; net: Net }>
} {
const netsWithSelectors = this.getTracePathNetSelectors().map(
(selector) => ({
selector,
net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
}),
)

const undefinedNets = nets.filter((n) => !n.net)
const undefinedNets = netsWithSelectors.filter((n) => !n.net)
if (undefinedNets.length > 0) {
this.renderError(
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`,
)
}

return nets
return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) }
}

/**
* Get all the traces that are connected in any degree to this trace, this is
* used during autorouting to routes to pass through traces connected to the
* same net.
*/
_getAllTracesConnectedToSameNet(): Trace[] {
const traces = this.getSubcircuit().selectAll("trace") as Trace[]

const myNets = this._findConnectedNets().nets
const myPorts = this._findConnectedPorts().ports ?? []

return traces.filter((t) => {
if (t === this) return false
const tNets = t._findConnectedNets().nets
const tPorts = t._findConnectedPorts().ports ?? []
return (
tNets.some((n) => myNets.includes(n)) ||
tPorts.some((p) => myPorts.includes(p))
)
})
}

/**
Expand All @@ -175,10 +206,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
* Determine if a trace is explicitly connected to a net (not via a port)
*/
_isExplicitlyConnectedToNet(net: Net) {
const nets = this._findConnectedNets().map((n) => n.net)
const nets = this._findConnectedNets().nets
return nets.includes(net)
}

doInitialCreateNetsFromProps(): void {
createNetsFromProps(this, this.getTracePathNetSelectors())
}

doInitialSourceTraceRender(): void {
const { db } = this.project!
const { _parsedProps: props, parent } = this
Expand All @@ -192,11 +227,11 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
this._findConnectedPorts()
if (!allPortsFound) return

const nets = this._findConnectedNets()
const nets = this._findConnectedNets().nets

const trace = db.source_trace.insert({
connected_source_port_ids: ports.map((p) => p.port.source_port_id!),
connected_source_net_ids: nets.map((n) => n.net.source_net_id!),
connected_source_net_ids: nets.map((n) => n.source_net_id!),
})

this.source_trace_id = trace.source_trace_id
Expand All @@ -213,7 +248,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {

if (!allPortsFound) return

const nets = this._findConnectedNets()
const nets = this._findConnectedNets().netsWithSelectors

if (ports.length === 0 && nets.length === 2) {
// Find the two optimal points to connect the two nets
Expand Down Expand Up @@ -254,7 +289,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
elm.type === "pcb_plated_hole" ||
elm.type === "pcb_hole" ||
elm.type === "source_port" ||
elm.type === "pcb_port",
elm.type === "pcb_port" ||
elm.type === "source_trace",
)

const source_trace = db.source_trace.get(this.source_trace_id!)!
Expand Down Expand Up @@ -296,44 +332,24 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
return
}

if (pcbRouteHints.length === 0) {
const { solution } = autoroute(
pcbElements.concat([
{
...source_trace,
// manually override b/c some of the ports may be connected via nets
// so they don't appear properly in the source_trace, we don't need
// to do this if the algorithm correctly looks at connected_source_net_ids
connected_source_port_ids: ports.map((p) => p.source_port_id!),
} as SourceTrace,
]),
)
// TODO for some reason, the solution gets duplicated inside ijump-astar
const inputPcbTrace = solution[0]

if (!inputPcbTrace) {
// TODO render error indicating we could not find a route
console.log(
`Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`,
)
return
}
const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)
let orderedRouteObjectives: PcbRouteObjective[] = []

this.pcb_trace_id = pcb_trace.pcb_trace_id
this._portsRoutedOnPcb = ports
return
if (pcbRouteHints.length === 0) {
orderedRouteObjectives = [
portToObjective(ports[0]),
portToObjective(ports[1]),
]
} else {
// When we have hints, we have to order the hints then route between each
// terminal of the trace and the hints
// TODO order based on proximity to ports
orderedRouteObjectives = [
portToObjective(ports[0]),
...pcbRouteHints,
portToObjective(ports[1]),
]
}

// When we have hints, we have to order the hints then route between each
// terminal of the trace and the hints
// TODO order based on proximity to ports
const orderedRouteObjectives: PcbRouteObjective[] = [
portToObjective(ports[0]),
...pcbRouteHints,
portToObjective(ports[1]),
]

// Hints can indicate where there should be a via, but the layer is allowed
// to be unspecified, therefore we need to find possible layer combinations
// to go to each hint and still route to the start and end points
Expand All @@ -356,6 +372,19 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
this.source_trace_id!,
)

const allConnectedTraceIds = this._getAllTracesConnectedToSameNet().map(
(t) => t.source_trace_id,
)
for (const obstacle of obstacles) {
if (
obstacle.connectedTo.some((connection) =>
allConnectedTraceIds.includes(connection),
)
) {
obstacle.connectedTo.push(this.source_trace_id!)
}
}

// TODO explore all candidate layer combinations if one fails
const candidateLayerSelections = candidateLayerCombinations[0].layer_path

Expand All @@ -377,6 +406,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
for (const [a, b] of pairs(orderedRoutePoints)) {
const BOUNDS_MARGIN = 2 //mm
const ijump = new IJumpAutorouter({
OBSTACLE_MARGIN: 0.3,
input: {
obstacles,
connections: [
Expand All @@ -396,8 +426,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
})
const traces = ijump.solveAndMapToTraces()
if (traces.length === 0) {
this.renderError(
`Could not find a route between ${a.x}, ${a.y} and ${b.x}, ${b.y}`,
console.log(
`Could not find a route between ${a} and ${b} for ${this} (TODO render error)`,
)
return
}
Expand All @@ -410,6 +440,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
route: mergeRoutes(routes),
source_trace_id: this.source_trace_id!,
})
this._portsRoutedOnPcb = ports
this.pcb_trace_id = pcb_trace.pcb_trace_id
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LAYER_SELECTION_PREFERENCE = ["top", "bottom", "inner1", "inner2"]
* top -> bottom -> bottom -> top
* bottom -> top -> top -> bottom
*/
interface CandidateTraceLayerCombination {
export interface CandidateTraceLayerCombination {
layer_path: string[]
}

Expand Down
1 change: 1 addition & 0 deletions lib/utils/autorouting/mergeRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function pdist(a: any, b: any) {
* reverse the next route and append it to the previous route.
*/
export const mergeRoutes = (routes: PCBTrace["route"][]) => {
if (routes.length === 1) return routes[0]
// routes = routes.filter((route) => route.length > 0)
if (routes.some((r) => r.length === 0)) {
throw new Error("Cannot merge routes with zero length")
Expand Down
15 changes: 15 additions & 0 deletions lib/utils/components/createNetsFromProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { PrimitiveComponent } from "lib/components/base-components/PrimitiveComponent"
import { Net } from "lib/components/primitive-components/Net"

export const createNetsFromProps = (
component: PrimitiveComponent,
props: (string | undefined | null)[],
) => {
for (const prop of props) {
if (typeof prop === "string" && prop.startsWith("net.")) {
if (!component.getSubcircuit().selectOne(prop)) {
component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }))
}
}
}
}
Loading

0 comments on commit 3462fda

Please sign in to comment.