seveibar/am3352-ram-dogbone-and-single-layer-route-test
AM3352BZCZ100 processor connected to a W631GG6MB-12 DDR3 memory chip through a routed, length-matched 16-bit DDR interface with clock/data-strobe pairs and control/address signals.
- Version
- 0.0.9
- License
- unset
- Stars
- 0
routing/coupled-plan.ts
import plan from "./coupled-route-plan.json"
type Point={x:number,y:number}
const dist=(a:Point,b:Point)=>Math.hypot(a.x-b.x,a.y-b.y)
export function expandPath(path:any):Point[]{
const points:Point[]=[{x:path.start[0],y:path.start[1]}]
for(const c of path.commands){
if(c[0]==="L")points.push({x:c[1],y:c[2]})
else if(c[0]==="A"){
const start=points.at(-1)!,vx=start.x-c[1],vy=start.y-c[2]
for(let j=1;j<=c[4];j++){const t=c[3]*j;points.push({x:c[1]+vx*Math.cos(t)-vy*Math.sin(t),y:c[2]+vx*Math.sin(t)+vy*Math.cos(t)})}
}else throw new Error("Unknown routing primitive")
}
return points
}
export function materializePlan(input:any,stage:"guide"|"tuned"|"simplified"){
if(input.connections.length!==plan.routes.length)throw new Error("Connection set changed; replan paired corridors")
for(const key of ["minX","maxX","minY","maxY"] as const){if(Math.abs(input.bounds[key]-plan.bounds[key])>1e-6)throw new Error("Board bounds changed; replan required")}
return plan.routes.map(r=>{
const connection=input.connections.find((c:any)=>c.name===r.name)
if(!connection||connection.pointsToConnect.length!==2)throw new Error(`Missing connection ${r.name}`)
const [a,b]=connection.pointsToConnect
for(const [i,p] of [a,b].entries()){
if(p.layer!=="top"||dist(p,{x:r.endpoints[i][0],y:r.endpoints[i][1]})>1e-6)throw new Error(`${r.name}: placement changed; replan required`)
}
if(Math.abs((connection.width??.1)-plan.traceWidth)>1e-8)throw new Error("Trace width changed; replan required")
const ps=expandPath(r[stage]),p=ps[0],q=ps.at(-1)!
for(const [pad,exit] of [[a,p],[b,q]])if(dist(exit,{x:pad.x+.401,y:pad.y-.4})>1e-6)throw new Error(`${r.name}: plan does not reach dogbone`)
const wire=(p:Point,layer:string,extra:any={})=>({route_type:"wire",...p,layer,width:.1,...extra})
const via=(p:Point,from_layer:string,to_layer:string)=>({route_type:"via",...p,from_layer,to_layer,via_diameter:.3,via_hole_diameter:.15})
return {type:"pcb_trace",pcb_trace_id:`signal_${r.name}`,connection_name:r.name,source_trace_id:connection.source_trace_id??r.name,
route:[wire({x:a.x,y:a.y},"top",{start_pcb_port_id:a.pcb_port_id}),wire(p,"top"),via(p,"top",r.layer),...ps.map(p=>wire(p,r.layer)),via(q,r.layer,"top"),wire(q,"top"),wire({x:b.x,y:b.y},"top",{end_pcb_port_id:b.pcb_port_id})]}
})
}
export function checkTiming(input:any,traces:any[]){
const lengths=new Map(traces.map(t=>[t.source_trace_id,t.route.slice(1).reduce((sum:number,p:any,i:number)=>{const q=t.route[i];return sum+(p.route_type==="wire"&&q.route_type==="wire"&&p.layer===q.layer?dist(p,q):0)},0)]))
for(const group of [...(input.buses??[]).map((g:any)=>({...g,tolerance:g.maxLengthSkew})),...(input.differentialPairs??[]).map((g:any)=>({...g,tolerance:g.lengthTolerance}))]){
const values=group.connectionNames.map((n:string)=>lengths.get(n))
if(values.some((v:any)=>v===undefined)||Math.max(...values)-Math.min(...values)>group.tolerance+1e-6)throw new Error(`Length skew fails for ${group.name??group.connectionNames.join(",")}`)
}
}