{
  "family": "Drive",
  "name": "A.2",
  "rev": "a",
  "tile_id": 13,
  "json_version": "0.11",
  "updated_at": "2026-05-01T12:42:45.081Z",
  "headline": "I2C/SPI-input dual 3W Class-D amplifier",
  "description": "The Drive.A.2 is a dual-channel audio output tile combining a DAC63202W 12-bit smart DAC with two TPA2028D1 Class-D audio power amplifiers. It provides two independent audio channels, each capable of delivering up to 3 W into 4 Ω or 880 mW into 8 Ω speakers.\n\nThe DAC63202W generates analog waveforms from digital data with 12-bit resolution and supports built-in waveform generation (sine, cosine, triangular, sawtooth) for processor-less tone generation. The DAC connects to a host processor via either I2C (up to 1 Mbps) or SPI (up to 50 MHz), auto-detected at power-on.\n\nEach TPA2028D1 amplifier features I2C-programmable gain control (−28 dB to +30 dB in 1 dB steps), automatic gain control (AGC), and dynamic range compression (DRC) to prevent speaker overdrive and enhance perceived loudness. The amplifiers operate filter-free in Class-D mode for high efficiency and include thermal and short-circuit protection.\n\nThe tile operates from a 2.5 V to 5.5 V supply. All three ICs are controlled via a shared I2C bus, while the DAC can alternatively be driven over SPI for higher-bandwidth waveform updates.",
  "application_notes": [
    {
      "sort": 0,
      "details": "The DAC in the Drive.A.2 tile automatically detects whether it is connected via I2C of SPI.  ",
      "heading": "Automatic Bus Detection",
      "image_url": ""
    },
    {
      "sort": 1,
      "details": "The Drive.A.2 can be configured for one of four I2C addresses by manipulating the connection of pad 3 (A0).  Leaving the pad floating (or connecting to V+) uses the default address of 0x49.  Connecting pad 3 to GND will change to address 0x48, while connecting to pad 3 to pad 4 (I2C.CLK) or pad 5 (I2C.DAT) will set the address to 0x4B or 0x4A, respectively.",
      "heading": "I2C Addresses",
      "image_url": ""
    }
  ],
  "package": {
    "pads": 10,
    "type": "T44",
    "size_x": 4000,
    "size_y": 4000,
    "size_z": 0
  },
  "power": [
    {
      "max": 5.5,
      "min": 2.5,
      "type": "system",
      "notes": "logic inputs can go down to 1.8V",
      "gnd_pad": [
        "1"
      ],
      "function": "",
      "direction": "input",
      "is_required": true,
      "max_current": "",
      "positive_pad": [
        "10"
      ]
    }
  ],
  "components": [
    {
      "url": "https://www.ti.com/product/DAC63202W",
      "part": "DAC63202W",
      "datasheet": "https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/13/Texas_Instruments-DAC63202W.pdf",
      "manufacturer": "Texas Instruments"
    },
    {
      "url": "https://www.ti.com/product/TPA2028D1",
      "part": "TPA2028D1",
      "datasheet": "https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/13/Texas_Instruments-TPA2028D1.pdf",
      "manufacturer": "Texas Instruments"
    }
  ],
  "pads": [
    {
      "pad": "1",
      "geometry": {
        "size_x": 1000,
        "size_y": 400,
        "center_x": -1500,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "",
          "type": "power",
          "function": "GND",
          "direction": "input"
        }
      ]
    },
    {
      "pad": "2",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 800
      },
      "functions": [
        {
          "note": "an internal 100k pull-up ",
          "type": "digital",
          "function": "GPIO",
          "direction": "bidirectional",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.MISO",
          "direction": "",
          "interface": "SPI"
        }
      ]
    },
    {
      "pad": "3",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 0
      },
      "functions": [
        {
          "note": "an internal 100k pull sets the default I2C address to 0x49.",
          "type": "digital",
          "function": "A0",
          "direction": "input",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.MOSI",
          "direction": "",
          "interface": "SPI"
        }
      ]
    },
    {
      "pad": "4",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": -800
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "I2C.CLK",
          "direction": "bidirectional",
          "interface": "I2C",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.CS",
          "direction": "",
          "interface": "SPI"
        }
      ]
    },
    {
      "pad": "5",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": -1600
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "I2C.DAT",
          "direction": "bidirectional",
          "interface": "I2C",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.CLK",
          "direction": "",
          "interface": "SPI"
        }
      ]
    },
    {
      "pad": "6",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -1600
      },
      "functions": [
        {
          "note": "",
          "type": "drive",
          "function": "OUT.0-",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "7",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -800
      },
      "functions": [
        {
          "note": "",
          "type": "drive",
          "function": "OUT.0+",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "8",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 0
      },
      "functions": [
        {
          "note": "",
          "type": "drive",
          "function": "OUT.1-",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "9",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 800
      },
      "functions": [
        {
          "note": "",
          "type": "drive",
          "function": "OUT.1+",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "10",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "2.5-5.5V",
          "type": "power",
          "function": "V+",
          "direction": "input"
        }
      ]
    }
  ],
  "interfaces": [
    {
      "name": "I2C",
      "type": "I2C",
      "parameters": {
        "modes": [
          "slave"
        ],
        "addresses": [
          {
            "address": "0x49",
            "is_default": true
          },
          {
            "address": "0x48"
          },
          {
            "address": "0x4A"
          },
          {
            "address": "0x4B"
          }
        ],
        "max_clock_speed": "1MHz"
      },
      "pad_assignments": [
        {
          "pad": "4",
          "role": "bus",
          "function": "I2C.CLK",
          "is_required": true
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "I2C.DAT",
          "is_required": true
        }
      ],
      "mutually_exclusive": [
        "SPI"
      ]
    },
    {
      "name": "SPI",
      "type": "SPI",
      "parameters": {
        "modes": [
          "slave"
        ],
        "max_clock_speed": "50MHz"
      },
      "pad_assignments": [
        {
          "pad": "2",
          "role": "bus",
          "function": "SPI.MISO",
          "is_required": true
        },
        {
          "pad": "3",
          "role": "bus",
          "function": "SPI.MOSI",
          "is_required": true
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "SPI.CLK",
          "is_required": true
        },
        {
          "pad": "4",
          "role": "other",
          "function": "SPI.CS",
          "is_required": true
        }
      ],
      "mutually_exclusive": [
        "I2C"
      ]
    }
  ],
  "twin": {
    "score": 1,
    "source": "// Digital twin for Drive.A.2 — TI DAC63202W smart DAC + 2× TPA2028D1 Class-D amps.\n//\n// Three ICs, one I²C bus, single V+ supply (pad 10, 2.5–5.5 V):\n//   • DAC63202W @ 0x49 (A0 pulled to VDD): two 12-bit DAC channels with an\n//     on-chip function generator (triangle/saw/sine), per-channel gain (VREF\n//     multiplier), slew/step/phase. CH0 → amp0, CH1 → amp1.\n//   • 2× TPA2028D1 @ 0x58 (fixed, SHARED): both amps answer the same address, so\n//     amp gain/enable/AGC writes hit BOTH. Each drives a speaker differentially\n//     (OUT.0± pads 7/6, OUT.1± pads 9/8).\n//\n// Signal chain per channel: DAC code → amp (gain dB) → Class-D differential out.\n// Speakers are external, so output level + audio power are MODELED; the amp\n// quiescent/shutdown currents and the 2.5 W / 90 % max-output point are canonical\n// (TPA2028D1 datasheet), as is the DAC conversion code = mv·4096/VREF.\nimport type { TileSim } from '../tileSim';\n\nconst DAC_ADDR = 0x49; // A0 → VDD\nconst DAC_MAX = 4095;\n\nconst WAVE_SINE = 4;\nconst WAVE_OFF = 7;\n\n// DAC full-scale (mV) per gain code (drive_a_2_gain_t): the VREF table\nconst GAIN_FS_MV = [3300, 3300, 1815, 2420, 3630, 4840];\n\n// supply currents on V+ (µA)\nconst I_DAC_ACTIVE_UA = 3000; // DAC63202W active (approx)\nconst I_DAC_SLEEP_UA = 50; // DAC power-down (approx)\nconst I_AMP_ACTIVE_UA = 2000; // TPA2028D1 quiescent, enabled (~@5 V) — datasheet\nconst I_AMP_SHUTDOWN_UA = 0.2; // TPA2028D1 EN-low shutdown — datasheet\nconst AMP_PMAX_W = 2.5; // max output per channel, 4 Ω / 5 V / 1% THD — datasheet\nconst AMP_EFF = 0.9; // Class-D efficiency ~90% — datasheet\n\ninterface State {\n  // ── DAC channel 0 ──\n  ch0_code: number; // 12-bit output code\n  ch0_gain: number; // gain/VREF select (0..5)\n  ch0_wave: number; // function-gen waveform (7 = off)\n  ch0_wave_on: number; // function generator running\n  ch0_freq: number; // tone frequency (Hz)\n  ch0_slew: number;\n  ch0_step: number;\n  ch0_phase: number;\n  ch0_mlo: number;\n  ch0_mhi: number;\n\n  // ── DAC channel 1 ──\n  ch1_code: number;\n  ch1_gain: number;\n  ch1_wave: number;\n  ch1_wave_on: number;\n  ch1_freq: number;\n  ch1_slew: number;\n  ch1_step: number;\n  ch1_phase: number;\n  ch1_mlo: number;\n  ch1_mhi: number;\n\n  // ── amps (shared) + tile ──\n  amp_gain_db: number; // -28..+30\n  amp_enabled: number;\n  amp_comp: number; // AGC compression ratio\n  amp_max_gain: number; // AGC max gain (dB)\n  muted: number;\n  vplus_mv: number;\n  sleeping: number;\n\n  [field: string]: number;\n}\n\nconst pick = (args: number[], i: number, cur: number) =>\n  args.length > i && Number.isFinite(args[i]) ? args[i] : cur;\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v));\n\n// Channels a tier-2 call touches (drive_a_2_channel_t: 0 left, 1 right, 2 both).\nconst chans = (ch: number) => (ch === 2 ? [0, 1] : [clamp(ch, 0, 1)]);\n\nconst codeOf = (s: State, ch: number) => (ch === 0 ? s.ch0_code : s.ch1_code);\n\n// Per-channel output drive level 0..1: DAC code × amp linear gain, clipped.\n// Zero unless the amps are enabled, un-muted, and the tile is awake.\nfunction outLevel(s: State, ch: number): number {\n  if (s.amp_enabled !== 1 || s.muted === 1 || s.sleeping === 1) return 0;\n  const norm = codeOf(s, ch) / DAC_MAX;\n  const lin = Math.pow(10, s.amp_gain_db / 20);\n  return clamp(norm * lin, 0, 1);\n}\n\n// Build a nextState patch applying {base: value} to one or both channels.\nfunction chPatch(ch: number, base: string, value: number): Partial<State> {\n  const out: Record<string, number> = {};\n  for (const c of chans(ch)) out[`ch${c}_${base}`] = value;\n  return out as Partial<State>;\n}\n\nconst sim: TileSim<State> = {\n  tile: 'Drive.A.2',\n\n  defaultState: {\n    ch0_code: 0,\n    ch0_gain: 0,\n    ch0_wave: WAVE_OFF,\n    ch0_wave_on: 0,\n    ch0_freq: 1000,\n    ch0_slew: 0,\n    ch0_step: 0,\n    ch0_phase: 0,\n    ch0_mlo: 0,\n    ch0_mhi: DAC_MAX,\n\n    ch1_code: 0,\n    ch1_gain: 0,\n    ch1_wave: WAVE_OFF,\n    ch1_wave_on: 0,\n    ch1_freq: 1000,\n    ch1_slew: 0,\n    ch1_step: 0,\n    ch1_phase: 0,\n    ch1_mlo: 0,\n    ch1_mhi: DAC_MAX,\n\n    amp_gain_db: 6,\n    amp_enabled: 1,\n    amp_comp: 2, // 4:1 default\n    amp_max_gain: 30,\n    muted: 0,\n    vplus_mv: 5000,\n    sleeping: 0,\n  },\n\n  controls: [\n    { type: 'slider', field: 'ch0_code', label: 'CH0 DAC code', min: 0, max: 4095, step: 1 },\n    { type: 'slider', field: 'ch1_code', label: 'CH1 DAC code', min: 0, max: 4095, step: 1 },\n    {\n      type: 'slider',\n      field: 'amp_gain_db',\n      label: 'Amp gain',\n      min: -28,\n      max: 30,\n      step: 1,\n      unit: 'dB',\n    },\n    {\n      type: 'slider',\n      field: 'vplus_mv',\n      label: 'V+ supply',\n      min: 2500,\n      max: 5500,\n      step: 50,\n      unit: 'mV',\n    },\n    { type: 'toggle', field: 'amp_enabled', label: 'Amps enabled' },\n    { type: 'toggle', field: 'muted', label: 'Mute' },\n    { type: 'toggle', field: 'ch0_wave_on', label: 'CH0 function gen' },\n    { type: 'toggle', field: 'ch1_wave_on', label: 'CH1 function gen' },\n    { type: 'toggle', field: 'sleeping', label: 'Sleep' },\n  ],\n\n  hostCalls: {\n    // ── lifecycle ──\n    tile_drive_a_2_find: () => ({ scalar: DAC_ADDR }),\n    tile_drive_a_2_init: () => ({\n      scalar: 0,\n      nextState: { amp_enabled: 1, amp_gain_db: 6, muted: 0, sleeping: 0 },\n    }),\n    tile_drive_a_2_sleep: () => ({ nextState: { sleeping: 1 } }),\n    tile_drive_a_2_wake: () => ({ nextState: { sleeping: 0 } }),\n    tile_drive_a_2_reset: () => ({\n      nextState: { ch0_code: 0, ch1_code: 0, ch0_wave_on: 0, ch1_wave_on: 0, muted: 0 },\n    }),\n\n    // ── DAC output ──\n    tile_drive_a_2_set: ({ args }) =>\n      chPatchResult(args, 'code', clamp(pick(args, 1, 0), 0, DAC_MAX)),\n    tile_drive_a_2_set_mv: ({ state, args }) => {\n      const ch = clamp(pick(args, 0, 0), 0, 1);\n      const gain = ch === 0 ? state.ch0_gain : state.ch1_gain;\n      const vref = GAIN_FS_MV[clamp(gain, 0, 5)];\n      const code = clamp(Math.round((pick(args, 1, 0) * 4096) / vref), 0, DAC_MAX);\n      return { nextState: chPatch(ch, 'code', code) };\n    },\n    tile_drive_a_2_get: ({ state, args }) => ({\n      scalar: codeOf(state, clamp(pick(args, 0, 0), 0, 1)),\n    }),\n    tile_drive_a_2_set_gain: ({ args }) =>\n      chPatchResult(args, 'gain', clamp(pick(args, 1, 0), 0, 5)),\n\n    // ── DAC function generator ──\n    tile_drive_a_2_set_waveform: ({ args }) => chPatchResult(args, 'wave', pick(args, 1, WAVE_OFF)),\n    tile_drive_a_2_start_waveform: ({ args }) => ({\n      nextState: chPatch(pick(args, 0, 0), 'wave_on', 1),\n    }),\n    tile_drive_a_2_stop_waveform: ({ args }) => ({\n      nextState: chPatch(pick(args, 0, 0), 'wave_on', 0),\n    }),\n    tile_drive_a_2_set_slew_rate: ({ args }) => chPatchResult(args, 'slew', pick(args, 1, 0)),\n    tile_drive_a_2_set_code_step: ({ args }) => chPatchResult(args, 'step', pick(args, 1, 0)),\n    tile_drive_a_2_set_margins: ({ args }) => {\n      const ch = pick(args, 0, 0);\n      return {\n        nextState: {\n          ...chPatch(ch, 'mlo', pick(args, 1, 0)),\n          ...chPatch(ch, 'mhi', pick(args, 2, DAC_MAX)),\n        },\n      };\n    },\n    tile_drive_a_2_set_phase: ({ args }) => chPatchResult(args, 'phase', pick(args, 1, 0)),\n    tile_drive_a_2_set_waveform_params: ({ args }) =>\n      chPatchResult(args, 'freq', pick(args, 1, 1000)),\n\n    // ── amps (shared 0x58 — affect both) ──\n    tile_drive_a_2_amp_set_gain: ({ args }) => ({\n      nextState: { amp_gain_db: clamp(pick(args, 0, 6), -28, 30) },\n    }),\n    tile_drive_a_2_amp_get_gain: ({ state }) => ({ scalar: state.amp_gain_db }),\n    tile_drive_a_2_amp_enable: () => ({ nextState: { amp_enabled: 1 } }),\n    tile_drive_a_2_amp_disable: () => ({ nextState: { amp_enabled: 0 } }),\n    tile_drive_a_2_amp_set_agc: ({ args }) => ({\n      nextState: {\n        amp_comp: clamp(pick(args, 0, 2), 0, 3),\n        amp_max_gain: clamp(pick(args, 1, 30), 18, 30),\n      },\n    }),\n    // FUNC_CTRL readback: bit5 ~ amp enabled (SWS=0); compression in low bits.\n    tile_drive_a_2_amp_read_status: ({ state }) => ({\n      scalar: (state.amp_enabled ? 0x20 : 0) | (state.amp_comp & 0x03),\n    }),\n\n    // ── status / NVM / raw ──\n    tile_drive_a_2_read_status: () => ({ scalar: 0 }), // no fault modeled\n    tile_drive_a_2_nvm_save: () => ({ nextState: {} }),\n    tile_drive_a_2_nvm_reload: () => ({ nextState: {} }),\n    tile_drive_a_2_read_reg: ({ args }) => ({ scalar: pick(args, 0, 0) === 0 ? DAC_ADDR : 0 }),\n    tile_drive_a_2_write_reg: () => ({ nextState: {} }),\n\n    // ── tier-2 helpers ──\n    tile_drive_a_2_play_tone: ({ args }) => {\n      const ch = pick(args, 0, 0);\n      return {\n        nextState: {\n          ...chPatch(ch, 'wave', WAVE_SINE),\n          ...chPatch(ch, 'wave_on', 1),\n          ...chPatch(ch, 'freq', pick(args, 1, 1000)),\n        },\n      };\n    },\n    tile_drive_a_2_play_silence: ({ args }) => {\n      const ch = pick(args, 0, 0);\n      return { nextState: { ...chPatch(ch, 'wave_on', 0), ...chPatch(ch, 'code', 2048) } };\n    },\n    tile_drive_a_2_play_chirp: ({ args }) => {\n      const ch = pick(args, 0, 0);\n      return { nextState: { ...chPatch(ch, 'wave', WAVE_SINE), ...chPatch(ch, 'wave_on', 1) } };\n    },\n    tile_drive_a_2_set_volume_pct: ({ args }) => ({\n      nextState: {\n        amp_gain_db: Math.round(-28 + clamp(pick(args, 1, 100), 0, 100) * 0.58),\n        muted: 0,\n      },\n    }),\n    tile_drive_a_2_mute: () => ({ nextState: { muted: 1 } }),\n    tile_drive_a_2_unmute: () => ({ nextState: { muted: 0 } }),\n  },\n\n  provenance: {\n    // canonical — datasheet/driver-accurate\n    tile_drive_a_2_find: 'canonical', // DAC I2C 0x49\n    tile_drive_a_2_set_mv: 'canonical', // code = mv·4096/VREF\n    tile_drive_a_2_get: 'canonical',\n    tile_drive_a_2_set_gain: 'canonical', // VREF multiplier table\n    tile_drive_a_2_set_waveform: 'canonical', // function-gen wave codes\n    tile_drive_a_2_amp_set_gain: 'canonical', // -28..+30 dB AGC gain reg\n    tile_drive_a_2_amp_enable: 'canonical',\n    tile_drive_a_2_amp_disable: 'canonical',\n    // inferred — obvious from the driver; signal/level modeled\n    tile_drive_a_2_init: 'inferred',\n    tile_drive_a_2_sleep: 'inferred',\n    tile_drive_a_2_wake: 'inferred',\n    tile_drive_a_2_reset: 'inferred',\n    tile_drive_a_2_set: 'inferred',\n    tile_drive_a_2_start_waveform: 'inferred',\n    tile_drive_a_2_stop_waveform: 'inferred',\n    tile_drive_a_2_set_slew_rate: 'inferred',\n    tile_drive_a_2_set_code_step: 'inferred',\n    tile_drive_a_2_set_margins: 'inferred',\n    tile_drive_a_2_set_phase: 'inferred',\n    tile_drive_a_2_set_waveform_params: 'inferred',\n    tile_drive_a_2_amp_get_gain: 'inferred',\n    tile_drive_a_2_amp_set_agc: 'inferred', // struct arg flattened to comp/max-gain\n    tile_drive_a_2_amp_read_status: 'inferred',\n    tile_drive_a_2_read_status: 'inferred',\n    tile_drive_a_2_nvm_save: 'inferred',\n    tile_drive_a_2_nvm_reload: 'inferred',\n    tile_drive_a_2_play_tone: 'inferred',\n    tile_drive_a_2_play_silence: 'inferred',\n    tile_drive_a_2_play_chirp: 'inferred',\n    tile_drive_a_2_set_volume_pct: 'inferred',\n    tile_drive_a_2_mute: 'inferred',\n    tile_drive_a_2_unmute: 'inferred',\n    // hallucinated — opaque register access\n    tile_drive_a_2_read_reg: 'hallucinated',\n    tile_drive_a_2_write_reg: 'hallucinated',\n    power: 'inferred', // audio output modeled; amp quiescent + max-output canonical\n  },\n\n  // Differential Class-D outputs: per-channel drive envelope 0..1.\n  padOutputs(state) {\n    const o0 = outLevel(state, 0);\n    const o1 = outLevel(state, 1);\n    return { 'OUT.0+': o0, 'OUT.0-': o0, 'OUT.1+': o1, 'OUT.1-': o1 };\n  },\n\n  // Electrical. Single V+ rail (pad 10) feeds the DAC + both amps. Audio output\n  // power dominates: per channel P_out = level²·Pmax(V+), drawn at ~90% Class-D\n  // efficiency. Speaker outputs on pads 7/6 (amp0) and 9/8 (amp1).\n  power(state) {\n    const vplus_v = state.vplus_mv / 1000;\n    let drawUa: number;\n    if (state.sleeping === 1) {\n      drawUa = I_DAC_SLEEP_UA + 2 * I_AMP_SHUTDOWN_UA;\n    } else {\n      const ampQ = state.amp_enabled === 1 ? 2 * I_AMP_ACTIVE_UA : 2 * I_AMP_SHUTDOWN_UA;\n      const pmax = AMP_PMAX_W * (vplus_v / 5) * (vplus_v / 5); // scales with V+²\n      const o0 = outLevel(state, 0);\n      const o1 = outLevel(state, 1);\n      const audioW = (o0 * o0 + o1 * o1) * pmax;\n      const audioUa = (audioW / (AMP_EFF * vplus_v)) * 1e6;\n      drawUa = I_DAC_ACTIVE_UA + ampQ + audioUa;\n    }\n\n    const o0 = outLevel(state, 0);\n    const o1 = outLevel(state, 1);\n    return {\n      draw_ua: Math.round(drawUa),\n      rails: [\n        {\n          name: 'V+',\n          role: 'supply',\n          v_mv: state.vplus_mv,\n          i_ua: Math.round(drawUa),\n          pads: ['10'],\n          note: 'DAC + 2× amp + audio',\n        },\n        {\n          name: 'OUT.0',\n          role: 'output',\n          v_mv: Math.round(o0 * state.vplus_mv),\n          pads: ['7', '6'],\n          note: 'amp0 Class-D speaker out',\n        },\n        {\n          name: 'OUT.1',\n          role: 'output',\n          v_mv: Math.round(o1 * state.vplus_mv),\n          pads: ['9', '8'],\n          note: 'amp1 Class-D speaker out',\n        },\n      ],\n    };\n  },\n};\n\n// Helper used by several DAC setters: apply args[1]-derived value to the channel\n// in args[0] (low-level DAC API uses channel 0/1).\nfunction chPatchResult(args: number[], base: string, value: number) {\n  const ch = args.length > 0 && Number.isFinite(args[0]) ? args[0] : 0;\n  const out: Record<string, number> = {};\n  for (const c of ch === 2 ? [0, 1] : [Math.max(0, Math.min(1, ch))]) out[`ch${c}_${base}`] = value;\n  return { nextState: out };\n}\n\nexport default sim;\n",
    "status": "validated",
    "updated_at": "2026-06-21 18:29:40"
  },
  "config": {
    "interfaceMode": {
      "kind": "select",
      "group": "Interfaces",
      "label": "Interface mode",
      "default": "i2c",
      "options": [
        {
          "label": "I2C",
          "value": "i2c",
          "activates": {
            "interface": "I2C"
          }
        },
        {
          "label": "SPI",
          "value": "spi",
          "activates": {
            "interface": "SPI"
          }
        }
      ],
      "description": "Selects which bus this tile sits on. The chip's mode is set by firmware via register write at init (no boot-strap pad on this tile), so the catalog choice expresses *wiring intent* and which interface's pad_assignments are active."
    }
  }
}