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/elastic-match.ts

import { length } from "./single-layer"
type Point = {x:number,y:number}
type Route = {name:string,points:Point[]}
const dot=(a:Float64Array,b:Float64Array)=>{let s=0;for(let i=0;i<a.length;i++)s+=a[i]*b[i];return s}

/** Jointly move neighboring traces to reserve tuning room, keeping all terminals fixed.
 * The objective balances centerline lengths and sampled copper clearances. A bounded
 * L-BFGS step cannot jump across another route; final geometry is checked separately.
 */
export function elasticMatch(routes:Route[], vias:any[], buses:any[], pairs:any[]) {
 const xy:number[]=[], owners:number[]=[], fixed:number[]=[], edges:[number,number,number][]=[], ids:number[][]=[]
 for(let ri=0;ri<routes.length;ri++){
  const r=routes[ri],indices:number[]=[]
  for(let j=1;j<r.points.length;j++){
   const a=r.points[j-1],b=r.points[j],n=Math.max(1,Math.ceil(Math.hypot(b.x-a.x,b.y-a.y)/.4))
   for(let k=0;k<n;k++){indices.push(xy.length/2);xy.push(a.x+(b.x-a.x)*k/n,a.y+(b.y-a.y)*k/n);owners.push(ri);fixed.push(0)}
  }
  indices.push(xy.length/2);xy.push(r.points.at(-1)!.x,r.points.at(-1)!.y);owners.push(ri);fixed.push(1);fixed[indices[0]]=1
  for(let k=1;k<indices.length;k++)edges.push([indices[k-1],indices[k],ri]);ids.push(indices)
 }
 const origin=new Float64Array(xy);let x=origin.slice()
 const targets=routes.map(r=>length(r.points))
 for(const group of [...buses,...pairs]){const members=routes.map((r,i)=>group.connectionNames.includes(r.name)?i:-1).filter(i=>i>=0);if(members.length<2)continue;const t=Math.max(...members.map(i=>targets[i]));for(const i of members)targets[i]=t}
 const vn=vias.map(v=>routes.findIndex(r=>r.name===v.net))
 for(let outer=0;outer<8;outer++){
  const base=x.slice(),samples:[number,number,number,number][]=[],sampleXY:number[]=[]
  for(const [a,b,owner] of edges){const n=Math.max(1,Math.ceil(Math.hypot(x[2*b]-x[2*a],x[2*b+1]-x[2*a+1])/.075));for(let k=0;k<n;k++){const t=k/n;samples.push([a,b,t,owner]);sampleXY.push(x[2*a]*(1-t)+x[2*b]*t,x[2*a+1]*(1-t)+x[2*b+1]*t)}}
  const contacts:[number,number][]=[],viaContacts:[number,number][]=[]
  for(let i=0;i<samples.length;i++){
   for(let j=i+1;j<samples.length;j++)if(samples[i][3]!==samples[j][3]&&Math.hypot(sampleXY[2*i]-sampleXY[2*j],sampleXY[2*i+1]-sampleXY[2*j+1])<.48)contacts.push([i,j])
   for(let j=0;j<vias.length;j++)if(samples[i][3]!==vn[j]&&Math.hypot(sampleXY[2*i]-vias[j].x,sampleXY[2*i+1]-vias[j].y)<.5)viaContacts.push([i,j])
  }
  const evaluate=(p:Float64Array)=>{
   const g=new Float64Array(p.length),lens=new Float64Array(routes.length),ds:number[]=[]
   for(const [a,b,r] of edges){const d=Math.max(1e-8,Math.hypot(p[2*b]-p[2*a],p[2*b+1]-p[2*a+1]));ds.push(d);lens[r]+=d}
   let value=0
   for(let r=0;r<routes.length;r++)value+=10*(lens[r]-targets[r])**2
   edges.forEach(([a,b,r],i)=>{const factor=20*(lens[r]-targets[r])/ds[i];for(let k=0;k<2;k++){const d=factor*(p[2*b+k]-p[2*a+k]);g[2*b+k]+=d;g[2*a+k]-=d}})
   const s=new Float64Array(samples.length*2),sg=new Float64Array(s.length)
   samples.forEach(([a,b,t],i)=>{for(let k=0;k<2;k++)s[2*i+k]=p[2*a+k]*(1-t)+p[2*b+k]*t})
   for(const [a,b] of contacts){const dx=s[2*a]-s[2*b],dy=s[2*a+1]-s[2*b+1],d=Math.max(1e-9,Math.hypot(dx,dy)),gap=Math.min(0,d-.214);value+=20000*gap*gap;const f=40000*gap/d;sg[2*a]+=f*dx;sg[2*a+1]+=f*dy;sg[2*b]-=f*dx;sg[2*b+1]-=f*dy}
   for(const [a,b] of viaContacts){const dx=s[2*a]-vias[b].x,dy=s[2*a+1]-vias[b].y,d=Math.max(1e-9,Math.hypot(dx,dy)),gap=Math.min(0,d-.312);value+=20000*gap*gap;const f=40000*gap/d;sg[2*a]+=f*dx;sg[2*a+1]+=f*dy}
   samples.forEach(([a,b,t],i)=>{for(let k=0;k<2;k++){g[2*a+k]+=sg[2*i+k]*(1-t);g[2*b+k]+=sg[2*i+k]*t}})
   for(let i=0;i<p.length;i++){value+=.001*(p[i]-origin[i])**2;g[i]+=.002*(p[i]-origin[i]);if(fixed[i>>1])g[i]=0}
   return {value,g,lens}
  }
  let result=evaluate(x);const history:{s:Float64Array,y:Float64Array,rho:number}[]=[]
  for(let iter=0;iter<500;iter++){
   const q=result.g.slice(),alpha:number[]=[]
   for(let j=history.length-1;j>=0;j--){const h=history[j];alpha[j]=h.rho*dot(h.s,q);for(let i=0;i<q.length;i++)q[i]-=alpha[j]*h.y[i]}
   const last=history.at(-1),scale=last?dot(last.s,last.y)/dot(last.y,last.y):.0001
   for(let i=0;i<q.length;i++)q[i]*=scale
   for(let j=0;j<history.length;j++){const h=history[j],beta=h.rho*dot(h.y,q);for(let i=0;i<q.length;i++)q[i]+=h.s[i]*(alpha[j]-beta)}
   let step=1,accepted=false
   for(let search=0;search<24;search++){
    const next=x.slice(),delta=new Float64Array(x.length)
    for(let i=0;i<x.length;i++){if(!fixed[i>>1])next[i]=Math.max(base[i]-.04,Math.min(base[i]+.04,x[i]-step*q[i]));delta[i]=next[i]-x[i]}
    const nextResult=evaluate(next)
    if(nextResult.value<result.value+1e-4*dot(result.g,delta)){
     const y=nextResult.g.map((v,i)=>v-result.g[i]),sy=dot(delta,y)
     if(sy>1e-14){history.push({s:delta,y,rho:1/sy});if(history.length>15)history.shift()}
     x=next;result=nextResult;accepted=true;break
    }
    step*=.5
   }
   if(!accepted)break
   if(result.value<.001)break
  }
  console.log(`Elastic length match: pass ${outer+1}, objective ${result.value.toFixed(6)}`)
  if(result.value<.001)break
 }
 for(let ri=0;ri<routes.length;ri++)routes[ri].points=ids[ri].map(i=>({x:x[2*i],y:x[2*i+1]}))
}