0hmX/am3352

This code suite comprises TypeScript scripts that analyze, verify, and assemble complex DDR memory interface hardware, focusing on physical routing, via and pad placement, electrical clearance, and physical constraints, often involving precise geometric calculations and consistent provenance tracking.

Version
1.0.5
License
unset
Stars
0

tests/ddr-integration.test.tsx

import { test, expect } from 'bun:test'
import { RootCircuit } from '@tscircuit/core'
import { adaptDdrMemoryPaths, DdrMemoryAdapter, getDdrMemoryConnections } from '../src/ddr-memory'
import { AM3352_PINS } from '../src/pin-map'
import type { FanoutTracePath } from '@tscircuit/props'

const sample: FanoutTracePath[] = [{connection:'U1.ball_B3', route:[
  {route_type:'wire',x:0,y:0,width:.12,layer:'top'},
  {route_type:'wire',x:.4,y:.4,width:.12,layer:'top'},
  {route_type:'via',x:.4,y:.4,from_layer:'top',to_layer:'inner1',via_diameter:.45,via_hole_diameter:.2},
  {route_type:'wire',x:.4,y:.4,width:.12,layer:'inner1'},
  {route_type:'wire',x:3,y:.4,width:.12,layer:'inner1'},
]}]

test('two x8 byte maps cover every CPU DDR bus pin without sharing data', () => {
 const a=getDdrMemoryConnections('CPU',0), b=getDdrMemoryConnections('CPU',1)
 expect(Object.keys(a).length).toBe(39)
 const valid=new Set(AM3352_PINS.map(p=>p.name))
 for(const target of [...Object.values(a),...Object.values(b)]) expect(valid.has(target.split('.').at(-1)!)).toBe(true)
 for(const terminal of ['DQ0','DQ7','DM','DQS_P','DQS_N']) expect(a[terminal]).not.toBe(b[terminal])
 for(const terminal of ['A15','BA2','CS_N','CK_P','RESET_N']) expect(a[terminal]).toBe(b[terminal])
})

test('adapter preserves caller geometry and emits ten-layer through-vias and correct exits', async () => {
 const adapted=adaptDdrMemoryPaths(sample)
 expect(sample[0]!.route.at(-1)).toHaveProperty('layer','inner1')
 expect(adapted[0]!.route.at(-1)).toHaveProperty('layer','inner2')
 const circuit=new RootCircuit()
 circuit.add(<board width={10} height={10} layers={10} schematicDisabled allowBlindAndBuriedVias={false}>
   <DdrMemoryAdapter name="RAM" paths={sample} pins={[{ball:'B3',pin:1,terminal:'DQ0',routed:true}]}
     footprint={<footprint><smtpad portHints={['pin1']} pcbX={0} pcbY={0} shape="circle" radius={.1919986}/></footprint>}/>
 </board>)
 await circuit.renderUntilSettled()
 const json=circuit.getCircuitJson()
 const vias=json.filter(e=>e.type==='pcb_via')
 expect(vias.length).toBe(1)
 expect(vias[0]!.outer_diameter).toBe(.4572)
 expect(vias[0]!.hole_diameter).toBe(.254)
 expect(vias[0]!.layers).toEqual(['top','inner1','inner2','inner3','inner4','inner5','inner6','inner7','inner8','bottom'])
 const exits=json.filter(e=>e.type==='pcb_breakout_point')
 expect(exits.length).toBe(1)
 expect(exits[0]!.layer).toBe('inner2')
 expect(exits[0]!.x).toBe(3)
 expect(json.filter(e=>e.type.endsWith('_error'))).toEqual([])
})

test('explicit six-layer source mapping rejects missing or collapsed source layers', () => {
 const source = structuredClone(sample)
 for (const point of source[0]!.route) {
  if (point.route_type === 'wire' && point.layer === 'inner1') point.layer='inner3'
  if (point.route_type === 'via') point.to_layer='inner3'
 }
 expect(() => adaptDdrMemoryPaths(source)).toThrow('Unsupported original RAM layer')
 const six = {top:'top',inner1:'inner1',inner2:'inner2',inner3:'inner4',inner4:'inner5',bottom:'bottom'}
 expect(adaptDdrMemoryPaths(source,six)[0]!.route.at(-1)).toHaveProperty('layer','inner4')
 expect(() => adaptDdrMemoryPaths(source,{...six,inner3:'inner2'})).toThrow('distinct copper layers')
 expect(() => adaptDdrMemoryPaths(source,{...six,top:'inner6'})).toThrow('outer copper layers')
})

test('asymmetric RAM boundary preserves package origin and transformed exits', async () => {
 for (const rotation of [0,90,180,270]) {
  const circuit=new RootCircuit()
  circuit.add(<board width={40} height={40} layers={10} schematicDisabled allowBlindAndBuriedVias={false}>
    <DdrMemoryAdapter name="RAM" paths={sample} pins={[{ball:'B3',pin:1,terminal:'DQ0',routed:true}]}
      pcbX={7} pcbY={-4} pcbRotation={rotation} boundary={{minX:-2,maxX:3,minY:-1,maxY:2}}
      footprint={<footprint><smtpad portHints={['pin1']} pcbX={0} pcbY={0} shape="circle" radius={.1919986}/></footprint>}/>
  </board>)
  await circuit.renderUntilSettled()
  const json=circuit.getCircuitJson()
  const angle=rotation*Math.PI/180
  const pad=json.find(e=>e.type==='pcb_smtpad' && e.shape==='circle')!
  if (pad.type!=='pcb_smtpad' || pad.shape!=='circle') throw Error('Missing circle pad')
  expect(pad.x).toBeCloseTo(7,6);expect(pad.y).toBeCloseTo(-4,6)
  const exit=json.find(e=>e.type==='pcb_breakout_point')!
  expect(exit.x).toBeCloseTo(7+3*Math.cos(angle)-.4*Math.sin(angle),6)
  expect(exit.y).toBeCloseTo(-4+3*Math.sin(angle)+.4*Math.cos(angle),6)
  expect(json.filter(e=>e.type.endsWith('_error'))).toEqual([])
 }
})

test('explicit width adaptation preserves defaults and rejects sub-rule geometry', () => {
 expect(adaptDdrMemoryPaths(sample)[0]!.route[0]).toHaveProperty('width',.12)
 const adapted=adaptDdrMemoryPaths(sample,undefined,.1168)
 for (const point of adapted[0]!.route) if (point.route_type==='wire') expect(point.width).toBe(.1168)
 expect(sample[0]!.route[0]).toHaveProperty('width',.12)
 for (const width of [.1,0,-1,NaN,Infinity]) expect(()=>adaptDdrMemoryPaths(sample,undefined,width)).toThrow('0.1016')
})