import assert from 'node:assert/strict'; import {conv2d, im2col, dot, requantize, maxPool2x2} from './model.mjs'; const x = [Array.from({length: 4}, (_, r) => Array.from({length: 4}, (_, c) => r * 4 + c + 1))]; const kernel = [[1, 0, -1], [1, 0, -1], [1, 0, -1]]; const weights = [[kernel]], bias = [8]; const reference = conv2d(x, weights, {bias}); const flattenedWeights = kernel.flat(); const rows = im2col(x, 3, 3); const accumulators = rows.map(row => dot(row, flattenedWeights) + bias[0]); assert.deepEqual(accumulators, reference[0].flat()); assert.deepEqual(accumulators, [2, 2, 2, 2]); const activated = reference[0].map(row => row.map(v => Math.max(0, requantize(v, 0.5)))); const pooled = maxPool2x2(activated); assert.deepEqual(activated, [[1, 1], [1, 1]]); assert.deepEqual(pooled, [[1]]); // Cycle estimate with explicit assumptions: all MAC operands local, no stalls, // one output at a time, K=9, two single-MAC PEs, no overlap with output write. const macs = rows.length * flattenedWeights.length; const peCount = 2, macCyclesPerOutput = Math.ceil(flattenedWeights.length / peCount); const modelCycles = rows.length * (macCyclesPerOutput + 1); const usefulSlots = macs, scheduledSlots = rows.length * macCyclesPerOutput * peCount; assert.equal(macs, 36); assert.equal(modelCycles, 24); assert.equal(usefulSlots / scheduledSlots, 0.9); const report = {shape:[1, 2, 2], accumulators, activated, pooled, macs, peCount, modelCycles, macSlotUtilization:usefulSlots / scheduledSlots, uniqueInputBytes:16, weightBytes:9, outputBytes:4, biasBytes:4, scope:'Software reference and schedule estimate only; no synthesis or DDS run'}; console.log(JSON.stringify(report, null, 2));