seveibar/f1c100s-dev-board

This code defines the hardware layout and schematic for a four-layer F1C100S-based module, including footprint placement, pin mappings, power regulation, decoupling capacitors, support components, and routing configurations, enabling automated PCB generation, routing, and verification.

Version
1.3.0
License
unset
Stars
0

scripts/verify.mjs

import fs from 'node:fs'
import assert from 'node:assert/strict'
const j=JSON.parse(fs.readFileSync('dist/index/circuit.json','utf8'))
const of=t=>j.filter(x=>x.type===t)
const parent=new Map()
const root=x=>{ if(!parent.has(x))parent.set(x,x); if(parent.get(x)!==x)parent.set(x,root(parent.get(x))); return parent.get(x) }
const join=(a,b)=>parent.set(root(a),root(b))
for(const t of of('source_trace')) {
  const ids=[...(t.connected_source_port_ids||[]),...(t.connected_source_net_ids||[])]
  ids.slice(1).forEach(id=>join(ids[0],id))
}
const comp=name=>{const c=of('source_component').filter(c=>c.name===name);assert.equal(c.length,1,`unique component ${name}`);return c[0]}
const port=(name,pin)=>{const c=comp(name);const p=of('source_port').find(p=>p.source_component_id===c.source_component_id && (p.pin_number===pin||p.name===pin));assert.ok(p,`${name}.${pin}`);return p.source_port_id}
const net=name=>{const n=of('source_net').find(n=>n.name===name);assert.ok(n,`net ${name}`);return n.source_net_id}
let checks=0
const expect=(name,pin,n)=>{assert.equal(root(port(name,pin)),root(net(n)),`${name}.${pin} -> ${n}`);checks++}
const rails=['BOARD_GND','V5_IN','V3V3','V1V8','V1V2']
assert.equal(new Set(rails.map(n=>root(net(n)))).size,5,'power domains are separate')
for(const [part,nets] of Object.entries({
 J_PROG:['V5_IN','USB_N','USB_P','BOARD_GND','UART_TX','UART_RX'],
 J_SPI:['BOARD_GND','V3V3','SPI1_CLK','SPI1_MOSI','SPI1_MISO','SPI1_CS'],
 J_I2C:['BOARD_GND','V3V3','I2C_SDA','I2C_SCL'],
 U_FLASH:['FLASH_CS','FLASH_MISO','FLASH_WP','BOARD_GND','FLASH_MOSI','FLASH_CLK','FLASH_HOLD','V3V3'],
 D_RGB:['V5_IN','K_BLUE','K_RED','K_GREEN'],
 JP_BOOT:['SOC_FLASH_CS','FLASH_CS']
})) nets.forEach((n,i)=>expect(part,i+1,n))
for(const [pin,n] of Object.entries({39:'SPI1_MISO',40:'SPI1_CLK',41:'SPI1_MOSI',42:'SPI1_CS',37:'I2C_SDA',38:'I2C_SCL',49:'UART_RX',48:'UART_TX',47:'LED_RED',46:'LED_GREEN',45:'LED_BLUE',68:'USB_N',69:'USB_P',70:'RESET_N'}))expect('U1',+pin,n)
for(const pin of [5,20,50,67,80,4,73])expect('U1',pin,'V3V3')
for(const pin of [30,31,32,34,36])expect('U1',pin,'V1V8')
for(const pin of [22,35,71])expect('U1',pin,'V1V2')
assert.notEqual(root(net('SOC_FLASH_CS')),root(net('FLASH_CS')),'removable boot shunt remains separate in PCB netlist')
for(const color of ['RED','GREEN','BLUE']){
 expect(`Q_${color}`,1,`BASE_${color}`);expect(`Q_${color}`,2,'BOARD_GND');expect(`Q_${color}`,3,`COL_${color}`)
}
expect('U_USB_ESD',2,'BOARD_GND');expect('U_USB_ESD',4,'USB_P');expect('U_USB_ESD',5,'V5_IN');expect('U_USB_ESD',6,'USB_N')
const board=of('pcb_board')[0];assert.equal(board.width,62);assert.equal(board.height,48)
const errors=j.filter(x=>x.type.endsWith('_error'))
const decouplers=of('source_component').filter(c=>/^C_D\d+$/.test(c.name));
assert.equal(decouplers.length,15,'one dedicated decoupler per processor supply pin');
for(const c of decouplers){
 const cp=port(c.name,1),target=port('U1',Number(c.name.slice(3)));
 const traces=of('source_trace').filter(t=>t.connected_source_port_ids.includes(cp));
 assert.equal(traces.length,1,`${c.name} supply has one explicit trace`);
 assert.deepEqual([...traces[0].connected_source_port_ids].sort(),[cp,target].sort(),`${c.name} connects directly to its assigned U1 pin`);
 assert.deepEqual(traces[0].connected_source_net_ids,[],`${c.name} supply does not attach directly to a generic net`);
 assert.equal(root(port(c.name,2)),root(net('BOARD_GND')),`${c.name} return reaches ground`);
}
const carrierTargets = Object.fromEntries([
 ...['U_3V3','U_1V8','U_1V2'].flatMap(n=>[[`C_${n}_IN`,[n,8]],[`C_${n}_OUT`,[n,1]]]),
 ['C_FLASH',['U_FLASH',8]],['C_ESD',['U_USB_ESD',5]],
]);
const pcbPort=(n,p)=>of('pcb_port').find(e=>e.source_port_id===port(n,p));
const distance=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
for(const [name,[target,pin]] of Object.entries(carrierTargets)) {
 const cp=port(name,1), tp=port(target,pin);
 assert.ok(of('source_trace').some(t=>t.connected_source_port_ids.length===2&&t.connected_source_port_ids.includes(cp)&&t.connected_source_port_ids.includes(tp)),`${name} targets ${target}.${pin}`);
 assert.ok(distance(pcbPort(name,1),pcbPort(target,pin))<2.5,`${name} supply pad within 2.5 mm`);
 assert.ok(distance(pcbPort(name,1),pcbPort(target,pin))<distance(pcbPort(name,2),pcbPort(target,pin)),`${name} faces target`);
}
let bulkAssertions=0;
for(const c of of('source_component').filter(c=>c.name.startsWith('C_B_') || ['C_TV_VRN','C_TV_VRP','C_TV_REF'].includes(c.name) || /^C_U_.*_(IN|OUT)$/.test(c.name))){
 const pcb=of('pcb_component').find(p=>p.source_component_id===c.source_component_id);
 const carrier=/^C_U_/.test(c.name);
 assert.ok(Math.max(pcb.width,pcb.height)>=2.8,`${c.name}: 0805 land pattern`);
 assert.equal(pcb.layer,carrier?'top':'bottom',`${c.name}: correct assembly side`);
 assert.equal(c.max_voltage_rating,carrier?16:25,`${c.name}: capacitor voltage rating`);
 assert.equal(c.manufacturer_part_number,carrier?'GRM21BR71C475KE51L':c.name==='C_B_VCC_DRAM'?'GRM219R71E105KA88D':'GRM21BR61E106KA73L',`${c.name}: selected part`);
 bulkAssertions+=4;
}
assert.equal(bulkAssertions,64,'all sixteen larger capacitors checked');
const breakouts=of("source_component").filter(c=>c.ftype==="simple_test_point");
assert.equal(breakouts.length,26,"only connected module terminals are broken out");
const report={bulkCapacitorAssertions:bulkAssertions,carrierPlacementAssertions:Object.keys(carrierTargets).length*3,dedicatedDecouplers:decouplers.length,decouplingAssertions:decouplers.length*4+1,selectedModuleBreakouts:breakouts.length,electricalAssertions:checks+2,pcbComponents:of('pcb_component').length,pcbTraces:of('pcb_trace').length,pcbVias:of('pcb_via').length,errorsByType:errors.reduce((a,e)=>(a[e.type]=(a[e.type]||0)+1,a),{}),errors:errors.map(e=>({type:e.type,message:e.message}))}
fs.writeFileSync('verification.json',JSON.stringify(report,null,2)+'\n')
console.log(JSON.stringify({...report,errors:undefined},null,2))
if(errors.length)process.exitCode=1