{
  "family": "Sense",
  "name": "I.6P6",
  "rev": "a",
  "tile_id": 22,
  "json_version": "0.15",
  "updated_at": "2026-05-01T12:19:55.595Z",
  "headline": "extended-range 6-DOF IMU",
  "description": "The Sense.I.6P is a precision 6-DOF inertial measurement unit based on the extended-range TDK Invensense ICM-42686P.  The 16-bit accelerometer full-scale range can be set to +/- 2g, 4g, 8g, 16g, or 32g with an output data rate of up between 1.5Hz and 32kHz, while the 16-bit gyro range can be set to +/- 15.6, 31.5, 62.5, 125, 250, 500, 1000, 2000, or 4000 degrees per second with an output data rate of 12.5Hz and 32kHz.  The sensor includes a user-programmable filters, on-board motion functions (pedometer, tilt, tap, raise, etc.), and a 2kB FIFO.  This IMU is functionally very similar to the Sense.I.6P8, which has lower gyro noise and better temperature stability but with lower max ranges of 16g and 2000 degrees per second.",
  "application_notes": [
    {
      "sort": 0,
      "details": "The Z axis of the IMU protrudes through the center of the tile, while the XY plane is approximately 1.0mm from the bottom surface of the tile, with the X axis pointing toward the left side (pads 1-5), as shown.",
      "heading": "Sensor Axes",
      "image_url": "https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/22/app-note-0.png"
    }
  ],
  "package": {
    "pads": 10,
    "type": "T44",
    "size_x": 4000,
    "size_y": 4000,
    "size_z": 0
  },
  "power": [
    {
      "max": 3.6,
      "min": 1.8,
      "type": "system",
      "notes": "",
      "gnd_pad": [
        "1"
      ],
      "function": "",
      "direction": "input",
      "is_required": true,
      "max_current": "",
      "positive_pad": [
        "10"
      ]
    }
  ],
  "components": [
    {
      "url": "https://invensense.tdk.com/products/motion-tracking/6-axis/icm-42686-p/",
      "part": "ICM-42686P",
      "datasheet": "https://mosaic-component-datasheets.s3.eu-north-1.amazonaws.com/22/TDK_Invensense-ICM-42686P.pdf",
      "manufacturer": "TDK Invensense"
    }
  ],
  "pads": [
    {
      "pad": "1",
      "geometry": {
        "size_x": 1000,
        "size_y": 400,
        "center_x": -1500,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "",
          "type": "power",
          "function": "GND",
          "direction": ""
        }
      ]
    },
    {
      "pad": "2",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 800
      },
      "functions": [
        {
          "note": "internal pull-up sets the I2C address to 0x69, ground for 0x68",
          "type": "digital",
          "function": "I2C.AD0",
          "direction": "input",
          "interface": "I2C",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.MISO",
          "direction": "output",
          "interface": "SPI"
        }
      ]
    },
    {
      "pad": "3",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": -1600,
        "center_y": 0
      },
      "functions": [
        {
          "note": "internal pull-up enables I2C/I3C, ground for SPI",
          "type": "digital",
          "function": "I2C.EN",
          "direction": "input",
          "interface": "I2C",
          "is_default": true
        },
        {
          "note": "",
          "type": "digital",
          "function": "SPI.CS",
          "direction": "input",
          "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.CLK",
          "direction": "input",
          "interface": "SPI"
        },
        {
          "note": "",
          "type": "digital",
          "function": "I3C.CLK",
          "direction": "bidirectional",
          "interface": "I3C"
        }
      ]
    },
    {
      "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.MOSI",
          "direction": "input",
          "interface": "SPI"
        },
        {
          "note": "",
          "type": "digital",
          "function": "I3C.DAT",
          "direction": "bidirectional",
          "interface": "I3C"
        }
      ]
    },
    {
      "pad": "6",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -1600
      },
      "functions": []
    },
    {
      "pad": "7",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": -800
      },
      "functions": []
    },
    {
      "pad": "8",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 0
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "INT2",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "9",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 800
      },
      "functions": [
        {
          "note": "",
          "type": "digital",
          "function": "INT1",
          "direction": "output"
        }
      ]
    },
    {
      "pad": "10",
      "geometry": {
        "size_x": 800,
        "size_y": 400,
        "center_x": 1600,
        "center_y": 1600
      },
      "functions": [
        {
          "note": "1.8-3.6V",
          "type": "power",
          "function": "V+",
          "direction": "input"
        }
      ]
    }
  ],
  "interfaces": [
    {
      "name": "I2C",
      "type": "I2C",
      "parameters": {
        "modes": [
          "slave"
        ],
        "addresses": [
          {
            "address": "0x69",
            "is_default": true
          },
          {
            "address": "0x68"
          }
        ],
        "address_bits": [
          7
        ]
      },
      "pad_assignments": [
        {
          "pad": "4",
          "role": "bus",
          "function": "I2C.CLK",
          "is_required": true
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "I2C.DAT",
          "is_required": true
        },
        {
          "pad": "2",
          "role": "strap",
          "function": "I2C.AD0"
        },
        {
          "pad": "3",
          "role": "strap",
          "function": "I2C.EN"
        }
      ],
      "mutually_exclusive": [
        "I3C",
        "SPI"
      ]
    },
    {
      "name": "I3C",
      "type": "I3C",
      "parameters": {
        "modes": [
          "slave"
        ],
        "max_clock_speed": "12.5MHz (SDR)"
      },
      "pad_assignments": [
        {
          "pad": "4",
          "role": "bus",
          "function": "I3C.CLK",
          "is_required": true
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "I3C.DAT",
          "is_required": true
        },
        {
          "pad": "3",
          "role": "strap",
          "function": "I3C.EN"
        }
      ],
      "mutually_exclusive": [
        "I2C",
        "SPI"
      ]
    },
    {
      "name": "SPI",
      "type": "SPI",
      "parameters": {
        "modes": [
          "slave"
        ],
        "max_clock_speed": "24MHz"
      },
      "pad_assignments": [
        {
          "pad": "2",
          "role": "bus",
          "function": "SPI.MISO",
          "is_required": true
        },
        {
          "pad": "3",
          "role": "other",
          "function": "SPI.CS",
          "is_required": true
        },
        {
          "pad": "4",
          "role": "bus",
          "function": "SPI.CLK",
          "is_required": true
        },
        {
          "pad": "5",
          "role": "bus",
          "function": "SPI.MOSI",
          "is_required": true
        }
      ],
      "mutually_exclusive": [
        "I2C",
        "I3C"
      ]
    }
  ],
  "twin": {
    "score": 1,
    "source": "// Digital twin for Sense.I.6P6 — TDK InvenSense ICM-42686P, a 6-axis IMU\n// (3-axis accel + 3-axis gyro + temperature) with FIFO, Wake-on-Motion, and\n// APEX tilt/tap/pedometer, exposing two interrupt pins (INT1, INT2).\n//\n// Model: the physical world (orientation/acceleration in mg, rotation rate in\n// dps, die temperature in °C) is driven by the controls. Host calls convert\n// that world into raw register values using the EXACT integer conversions from\n// the driver (drivers/tile_sense_i_6p6.c), so firmware reading the tile gets\n// the same numbers it would from real hardware. The INT pins assert when a\n// routed condition (data-ready / FIFO watermark / WOM / tilt) is true.\n//\n// A few config fields (WOM enable + threshold, INT routing, tilt enable) are\n// surfaced as controls too, so the behavior of the output pins can be exercised\n// here without a firmware program. In real use, firmware sets these via the\n// set_*/int*/wom_* host calls, which write the very same state fields.\nimport type { TileSim } from '../tileSim';\n\ninterface State {\n  // ── physical stimulus (the world the IMU senses) ──\n  ax_mg: number;\n  ay_mg: number;\n  az_mg: number;\n  gx_dps: number;\n  gy_dps: number;\n  gz_dps: number;\n  temp_c: number;\n\n  // ── configuration (driver init defaults: ±8g, ±1000dps, 100Hz, low-noise) ──\n  accel_range: number; // 0:±32g 1:±16g 2:±8g 3:±4g 4:±2g\n  gyro_range: number; // 0:±4000 1:±2000 2:±1000 3:±500 4:±250 5:±125 6:±62.5 7:±31.25 dps\n  odr_code: number; // ODR enum (display only)\n  accel_pm: number; // 0:off 2:low-power 3:low-noise\n  gyro_pm: number; // 0:off 1:standby 3:low-noise\n  temp_enabled: number;\n\n  // ── interrupt routing + electrical config ──\n  int1_drdy: number;\n  int1_fifo: number;\n  int1_wom: number;\n  int2_drdy: number;\n  int2_fifo: number;\n  int2_wom: number;\n  int_active_high: number; // 1 = active-high (default), 0 = active-low\n  int_latched: number;\n\n  // ── wake-on-motion ──\n  wom_enabled: number;\n  wom_thr_mg: number;\n\n  // ── APEX (DMP) features ──\n  tilt_enabled: number;\n  ped_enabled: number;\n  tap_enabled: number;\n  step_count: number;\n  step_cadence: number; // u6.2 fixed point (÷4 → steps/s)\n\n  // ── FIFO ──\n  fifo_mode: number; // 0:bypass 1:stream 2:stop-on-full\n  fifo_watermark: number;\n  fifo_count: number;\n  fifo_lost: number;\n\n  // Open index signature (satisfies TileSim's SimState) — also holds a few\n  // write-only config scratch fields the driver has but the model doesn't read.\n  [field: string]: number;\n}\n\nconst WHO_AM_I = 0x44;\nconst I2C_ADDR_DEFAULT = 0x69;\n\n// ── exact conversions from the driver ────────────────────────────────────────\n\n// accel_lsb_per_g(): ±32g→1024 … ±2g→16384\nconst ACCEL_LSB_PER_G = [1024, 2048, 4096, 8192, 16384];\nfunction accelLsbPerG(range: number): number {\n  return ACCEL_LSB_PER_G[Math.max(0, Math.min(4, range | 0))];\n}\n// gyro LSB/dps table, ±4000dps→8.2 … ±31.25dps→1048.6\nconst GYRO_LSB_PER_DPS = [8.2, 16.4, 32.8, 65.5, 131.0, 262.0, 524.3, 1048.6];\nfunction gyroLsbPerDps(range: number): number {\n  return GYRO_LSB_PER_DPS[Math.max(0, Math.min(7, range | 0))];\n}\n\nfunction clampI16(v: number): number {\n  const r = Math.round(v);\n  return r > 32767 ? 32767 : r < -32768 ? -32768 : r;\n}\nconst rawAccel = (mg: number, range: number) => clampI16((mg / 1000) * accelLsbPerG(range));\nconst rawGyro = (dps: number, range: number) => clampI16(dps * gyroLsbPerDps(range));\n// get_temperature returns the raw int16; degC = raw/132.48 + 25.0\nconst rawTemp = (c: number) => clampI16((c - 25.0) * 132.48);\n\n// ── derived physical quantities ──────────────────────────────────────────────\n\nconst accelMagMg = (s: State) => Math.hypot(s.ax_mg, s.ay_mg, s.az_mg);\n// Sensors powered (low-power or low-noise) ⇒ data is being produced.\nconst sensorsOn = (s: State) => s.accel_pm >= 2 || s.gyro_pm >= 3;\n// WOM is high-passed acceleration: deviation of |a| from 1g, vs threshold.\nconst motionMg = (s: State) => Math.abs(accelMagMg(s) - 1000);\nconst womActive = (s: State) => s.wom_enabled === 1 && motionMg(s) > s.wom_thr_mg;\n// Tilt-detect: angle of the device from its face-up reference (+Z), in degrees.\nfunction tiltFromVerticalDeg(s: State): number {\n  const mag = accelMagMg(s) || 1;\n  const c = Math.max(-1, Math.min(1, s.az_mg / mag));\n  return (Math.acos(c) * 180) / Math.PI;\n}\nconst tiltActive = (s: State) => s.tilt_enabled === 1 && tiltFromVerticalDeg(s) > 35;\nconst dataReady = (s: State) => (sensorsOn(s) ? 1 : 0);\nconst fifoThsActive = (s: State) => s.fifo_mode !== 0 && s.fifo_count >= s.fifo_watermark;\n\n// Inclination of one axis from horizontal, in centi-degrees (driver helper).\nfunction tiltAxisCentiDeg(s: State, axis: number): number {\n  const a = [s.ax_mg, s.ay_mg, s.az_mg];\n  const target = a[axis % 3];\n  const o0 = a[(axis + 1) % 3];\n  const o1 = a[(axis + 2) % 3];\n  const perp = Math.hypot(o0, o1);\n  return Math.round((Math.atan2(target, perp) * 18000) / Math.PI);\n}\n\n// INT status byte (clear-on-read register, modeled as the live condition).\nfunction intStatus(s: State): number {\n  return (dataReady(s) ? 0x08 : 0) | (fifoThsActive(s) ? 0x04 : 0);\n}\n\n// arg-or-current helper for setters (called with [] in the read-out → harmless\n// idempotent re-apply; called with a value by firmware → applies it).\nconst pick = (args: number[], i: number, cur: number) =>\n  args.length > i && Number.isFinite(args[i]) ? args[i] : cur;\n\nconst sim: TileSim<State> = {\n  tile: 'Sense.I.6P6',\n\n  defaultState: {\n    ax_mg: 0,\n    ay_mg: 0,\n    az_mg: 1000, // resting flat, face-up: 1g on +Z\n    gx_dps: 0,\n    gy_dps: 0,\n    gz_dps: 0,\n    temp_c: 25,\n\n    accel_range: 2, // ±8g\n    gyro_range: 2, // ±1000 dps\n    odr_code: 6, // 100 Hz\n    accel_pm: 3, // low-noise\n    gyro_pm: 3, // low-noise\n    temp_enabled: 1,\n\n    int1_drdy: 0,\n    int1_fifo: 0,\n    int1_wom: 0,\n    int2_drdy: 0,\n    int2_fifo: 0,\n    int2_wom: 0,\n    int_active_high: 1,\n    int_latched: 0,\n\n    wom_enabled: 0,\n    wom_thr_mg: 200,\n\n    tilt_enabled: 0,\n    ped_enabled: 0,\n    tap_enabled: 0,\n    step_count: 0,\n    step_cadence: 0,\n\n    fifo_mode: 0,\n    fifo_watermark: 1,\n    fifo_count: 0,\n    fifo_lost: 0,\n  },\n\n  controls: [\n    // physical stimulus\n    {\n      type: 'slider',\n      field: 'ax_mg',\n      label: 'Accel X',\n      min: -2000,\n      max: 2000,\n      step: 10,\n      unit: 'mg',\n    },\n    {\n      type: 'slider',\n      field: 'ay_mg',\n      label: 'Accel Y',\n      min: -2000,\n      max: 2000,\n      step: 10,\n      unit: 'mg',\n    },\n    {\n      type: 'slider',\n      field: 'az_mg',\n      label: 'Accel Z',\n      min: -2000,\n      max: 2000,\n      step: 10,\n      unit: 'mg',\n    },\n    {\n      type: 'slider',\n      field: 'gx_dps',\n      label: 'Gyro X',\n      min: -2000,\n      max: 2000,\n      step: 5,\n      unit: 'dps',\n    },\n    {\n      type: 'slider',\n      field: 'gy_dps',\n      label: 'Gyro Y',\n      min: -2000,\n      max: 2000,\n      step: 5,\n      unit: 'dps',\n    },\n    {\n      type: 'slider',\n      field: 'gz_dps',\n      label: 'Gyro Z',\n      min: -2000,\n      max: 2000,\n      step: 5,\n      unit: 'dps',\n    },\n    {\n      type: 'slider',\n      field: 'temp_c',\n      label: 'Temperature',\n      min: -40,\n      max: 85,\n      step: 1,\n      unit: '°C',\n    },\n    // config knobs (firmware sets these via host calls; exposed here to exercise outputs)\n    { type: 'slider', field: 'wom_enabled', label: 'WOM enable', min: 0, max: 1, step: 1 },\n    {\n      type: 'slider',\n      field: 'wom_thr_mg',\n      label: 'WOM threshold',\n      min: 0,\n      max: 1000,\n      step: 5,\n      unit: 'mg',\n    },\n    { type: 'slider', field: 'int1_wom', label: 'Route WOM→INT1', min: 0, max: 1, step: 1 },\n    { type: 'slider', field: 'int1_drdy', label: 'Route DRDY→INT1', min: 0, max: 1, step: 1 },\n    {\n      type: 'slider',\n      field: 'tilt_enabled',\n      label: 'Tilt detect (→INT1)',\n      min: 0,\n      max: 1,\n      step: 1,\n    },\n  ],\n\n  hostCalls: {\n    // ── lifecycle ──\n    tile_sense_i_6p6_find: () => ({ scalar: I2C_ADDR_DEFAULT }),\n    tile_sense_i_6p6_init: () => ({\n      scalar: 0,\n      nextState: { accel_range: 2, gyro_range: 2, odr_code: 6, accel_pm: 3, gyro_pm: 3 },\n    }),\n    tile_sense_i_6p6_sleep: () => ({ nextState: { accel_pm: 0, gyro_pm: 0 } }),\n    tile_sense_i_6p6_wake: () => ({ nextState: { accel_pm: 3, gyro_pm: 3 } }),\n    tile_sense_i_6p6_reset: () => ({ scalar: 0 }),\n    tile_sense_i_6p6_subsystem_reset: () => ({ nextState: { fifo_count: 0 } }),\n    tile_sense_i_6p6_process: ({ state }) => ({ scalar: intStatus(state) }),\n    tile_sense_i_6p6_on_event: () => ({ nextState: {} }),\n\n    // ── configuration ──\n    tile_sense_i_6p6_set_accel_range: ({ state, args }) => ({\n      nextState: { accel_range: pick(args, 0, state.accel_range) },\n    }),\n    tile_sense_i_6p6_set_gyro_range: ({ state, args }) => ({\n      nextState: { gyro_range: pick(args, 0, state.gyro_range) },\n    }),\n    tile_sense_i_6p6_set_accel_odr: ({ state, args }) => ({\n      nextState: { odr_code: pick(args, 0, state.odr_code) },\n    }),\n    tile_sense_i_6p6_set_gyro_odr: ({ state, args }) => ({\n      nextState: { odr_code: pick(args, 0, state.odr_code) },\n    }),\n    tile_sense_i_6p6_set_power_mode: ({ state, args }) => ({\n      nextState: { accel_pm: pick(args, 0, state.accel_pm), gyro_pm: pick(args, 1, state.gyro_pm) },\n    }),\n    tile_sense_i_6p6_set_filter_bw: ({ args }) => ({ nextState: { _filter_bw: pick(args, 0, 0) } }),\n    tile_sense_i_6p6_set_filter_order: ({ args }) => ({\n      nextState: { _filter_order: pick(args, 0, 0) },\n    }),\n    tile_sense_i_6p6_set_temp_filter: ({ args }) => ({\n      nextState: { _temp_filter: pick(args, 0, 0) },\n    }),\n    tile_sense_i_6p6_set_temp_enabled: ({ state, args }) => ({\n      nextState: { temp_enabled: pick(args, 0, state.temp_enabled) },\n    }),\n\n    // ── data reads (raw register values, exactly as firmware would get) ──\n    tile_sense_i_6p6_data_ready: ({ state }) => ({ scalar: dataReady(state) }),\n    tile_sense_i_6p6_get_raw_accels: ({ state }) => ({\n      array: [\n        rawAccel(state.ax_mg, state.accel_range),\n        rawAccel(state.ay_mg, state.accel_range),\n        rawAccel(state.az_mg, state.accel_range),\n      ],\n    }),\n    tile_sense_i_6p6_get_raw_gyros: ({ state }) => ({\n      array: [\n        rawGyro(state.gx_dps, state.gyro_range),\n        rawGyro(state.gy_dps, state.gyro_range),\n        rawGyro(state.gz_dps, state.gyro_range),\n      ],\n    }),\n    tile_sense_i_6p6_get_raw_6dof: ({ state }) => ({\n      array: [\n        rawAccel(state.ax_mg, state.accel_range),\n        rawAccel(state.ay_mg, state.accel_range),\n        rawAccel(state.az_mg, state.accel_range),\n        rawGyro(state.gx_dps, state.gyro_range),\n        rawGyro(state.gy_dps, state.gyro_range),\n        rawGyro(state.gz_dps, state.gyro_range),\n      ],\n    }),\n    tile_sense_i_6p6_get_temperature: ({ state }) => ({ scalar: rawTemp(state.temp_c) }),\n    tile_sense_i_6p6_get_raw_all: ({ state }) => ({\n      array: [\n        rawTemp(state.temp_c),\n        rawAccel(state.ax_mg, state.accel_range),\n        rawAccel(state.ay_mg, state.accel_range),\n        rawAccel(state.az_mg, state.accel_range),\n        rawGyro(state.gx_dps, state.gyro_range),\n        rawGyro(state.gy_dps, state.gyro_range),\n        rawGyro(state.gz_dps, state.gyro_range),\n      ],\n    }),\n\n    // ── orientation / motion helpers (driver integer math) ──\n    tile_sense_i_6p6_is_face_up: ({ state }) => ({\n      scalar:\n        Math.abs(state.az_mg - 1000) <= 200 &&\n        Math.abs(state.ax_mg) <= 300 &&\n        Math.abs(state.ay_mg) <= 300\n          ? 1\n          : 0,\n    }),\n    tile_sense_i_6p6_is_face_down: ({ state }) => ({\n      scalar:\n        Math.abs(state.az_mg + 1000) <= 200 &&\n        Math.abs(state.ax_mg) <= 300 &&\n        Math.abs(state.ay_mg) <= 300\n          ? 1\n          : 0,\n    }),\n    tile_sense_i_6p6_is_moving: ({ state, args }) => ({\n      scalar: motionMg(state) > pick(args, 0, 50) ? 1 : 0,\n    }),\n    tile_sense_i_6p6_read_tilt_centi_degrees: ({ state, args }) => ({\n      scalar: tiltAxisCentiDeg(state, pick(args, 0, 2)),\n    }),\n    tile_sense_i_6p6_wait_for_tap: ({ state }) => ({ scalar: state.tap_enabled }),\n    tile_sense_i_6p6_wait_for_motion: ({ state }) => ({ scalar: motionMg(state) > 50 ? 1 : 0 }),\n\n    // ── interrupts ──\n    tile_sense_i_6p6_int1_config: ({ state, args }) => ({\n      nextState: {\n        int_active_high: args.length ? (args[0] & 0x01 ? 1 : 0) : state.int_active_high,\n        int_latched: args.length ? (args[0] & 0x04 ? 1 : 0) : state.int_latched,\n      },\n    }),\n    tile_sense_i_6p6_int2_config: ({ state, args }) => ({\n      nextState: {\n        int_active_high: args.length ? (args[0] & 0x01 ? 1 : 0) : state.int_active_high,\n        int_latched: args.length ? (args[0] & 0x04 ? 1 : 0) : state.int_latched,\n      },\n    }),\n    tile_sense_i_6p6_int1_data_ready: ({ state, args }) => ({\n      nextState: { int1_drdy: pick(args, 0, state.int1_drdy) },\n    }),\n    tile_sense_i_6p6_int1_fifo_ths: ({ state, args }) => ({\n      nextState: { int1_fifo: pick(args, 0, state.int1_fifo) },\n    }),\n    tile_sense_i_6p6_int1_wom: ({ state, args }) => ({\n      nextState: { int1_wom: pick(args, 0, state.int1_wom) },\n    }),\n    tile_sense_i_6p6_int2_data_ready: ({ state, args }) => ({\n      nextState: { int2_drdy: pick(args, 0, state.int2_drdy) },\n    }),\n    tile_sense_i_6p6_int2_fifo_ths: ({ state, args }) => ({\n      nextState: { int2_fifo: pick(args, 0, state.int2_fifo) },\n    }),\n    tile_sense_i_6p6_int2_wom: ({ state, args }) => ({\n      nextState: { int2_wom: pick(args, 0, state.int2_wom) },\n    }),\n    tile_sense_i_6p6_set_int_pulse_duration: ({ args }) => ({\n      nextState: { _int_pulse: pick(args, 0, 0) },\n    }),\n    tile_sense_i_6p6_get_int_status: ({ state }) => ({ scalar: intStatus(state) }),\n    tile_sense_i_6p6_get_int_status2: ({ state }) => ({ scalar: womActive(state) ? 0x07 : 0 }),\n    tile_sense_i_6p6_get_int_status3: ({ state }) => ({ scalar: tiltActive(state) ? 0x08 : 0 }),\n\n    // ── wake-on-motion ──\n    tile_sense_i_6p6_wom_config: ({ state, args }) => ({\n      nextState: { wom_thr_mg: pick(args, 0, state.wom_thr_mg) },\n    }),\n    tile_sense_i_6p6_wom_enable: () => ({ nextState: { wom_enabled: 1 } }),\n    tile_sense_i_6p6_wom_disable: () => ({ nextState: { wom_enabled: 0 } }),\n    tile_sense_i_6p6_smd_config: ({ args }) => ({ nextState: { _smd: pick(args, 0, 0) } }),\n\n    // ── APEX: pedometer ──\n    tile_sense_i_6p6_pedometer_enable: () => ({ nextState: { ped_enabled: 1 } }),\n    tile_sense_i_6p6_pedometer_disable: () => ({ nextState: { ped_enabled: 0 } }),\n    tile_sense_i_6p6_get_step_count: ({ state }) => ({ scalar: state.step_count }),\n    tile_sense_i_6p6_get_step_cadence: ({ state }) => ({ scalar: state.step_cadence }),\n    tile_sense_i_6p6_get_activity: ({ state }) => ({\n      scalar: state.ped_enabled && state.step_cadence > 0 ? 1 : 0,\n    }),\n\n    // ── APEX: tilt ──\n    tile_sense_i_6p6_tilt_enable: () => ({ nextState: { tilt_enabled: 1 } }),\n    tile_sense_i_6p6_tilt_disable: () => ({ nextState: { tilt_enabled: 0 } }),\n\n    // ── APEX: tap ──\n    tile_sense_i_6p6_tap_enable: () => ({ nextState: { tap_enabled: 1 } }),\n    tile_sense_i_6p6_tap_disable: () => ({ nextState: { tap_enabled: 0 } }),\n    tile_sense_i_6p6_get_tap_result: () => ({ array: [0, 0, 0] }),\n    tile_sense_i_6p6_get_tap_result_flat: () => ({ scalar: 0 }),\n\n    // ── FIFO ──\n    tile_sense_i_6p6_fifo_config: ({ state, args }) => ({\n      nextState: { fifo_mode: pick(args, 0, state.fifo_mode) },\n    }),\n    tile_sense_i_6p6_fifo_set_watermark: ({ state, args }) => ({\n      nextState: { fifo_watermark: Math.max(1, pick(args, 0, state.fifo_watermark)) },\n    }),\n    tile_sense_i_6p6_fifo_flush: () => ({ nextState: { fifo_count: 0 } }),\n    tile_sense_i_6p6_fifo_count: ({ state }) => ({ scalar: state.fifo_count }),\n    tile_sense_i_6p6_fifo_lost_count: ({ state }) => ({ scalar: state.fifo_lost }),\n    tile_sense_i_6p6_fifo_read_packet: ({ state }) => ({\n      array: [\n        rawAccel(state.ax_mg, state.accel_range),\n        rawAccel(state.ay_mg, state.accel_range),\n        rawAccel(state.az_mg, state.accel_range),\n        rawGyro(state.gx_dps, state.gyro_range),\n        rawGyro(state.gy_dps, state.gyro_range),\n        rawGyro(state.gz_dps, state.gyro_range),\n      ],\n    }),\n    tile_sense_i_6p6_fifo_read_packet_flat: ({ state }) => ({\n      array: [\n        rawAccel(state.ax_mg, state.accel_range),\n        rawAccel(state.ay_mg, state.accel_range),\n        rawAccel(state.az_mg, state.accel_range),\n      ],\n    }),\n    tile_sense_i_6p6_fifo_read_packets: ({ state }) => ({ scalar: state.fifo_count }),\n    tile_sense_i_6p6_fifo_read_packets_flat: ({ state }) => ({ scalar: state.fifo_count }),\n\n    // ── advanced ──\n    tile_sense_i_6p6_set_gyro_offset: ({ args }) => ({ nextState: { _goff: pick(args, 0, 0) } }),\n    tile_sense_i_6p6_set_accel_offset: ({ args }) => ({ nextState: { _aoff: pick(args, 0, 0) } }),\n    tile_sense_i_6p6_self_test: () => ({ scalar: 1 }),\n    tile_sense_i_6p6_read_reg: ({ args }) => ({ scalar: pick(args, 0, 0) === 0x75 ? WHO_AM_I : 0 }),\n    tile_sense_i_6p6_write_reg: ({ args }) => ({ nextState: { _last_reg: pick(args, 1, 0) } }),\n  },\n\n  // Provenance per host call (unlisted ⇒ \"inferred\"). Canonical = the number,\n  // bit layout, or formula is verified against the ICM-42686P datasheet;\n  // hallucinated = a placeholder whose behavior isn't really modeled yet.\n  provenance: {\n    // canonical — datasheet conversions / register bits\n    tile_sense_i_6p6_find: 'canonical', // I2C addr 0x69\n    tile_sense_i_6p6_get_raw_accels: 'canonical', // LSB/g table\n    tile_sense_i_6p6_get_raw_gyros: 'canonical', // LSB/dps table\n    tile_sense_i_6p6_get_raw_6dof: 'canonical',\n    tile_sense_i_6p6_get_raw_all: 'canonical',\n    tile_sense_i_6p6_get_temperature: 'canonical', // raw/132.48 + 25\n    tile_sense_i_6p6_set_accel_range: 'canonical', // ACCEL_FS_SEL\n    tile_sense_i_6p6_set_gyro_range: 'canonical', // GYRO_FS_SEL\n    tile_sense_i_6p6_set_power_mode: 'canonical', // PWR_MGMT0\n    tile_sense_i_6p6_data_ready: 'canonical',\n    tile_sense_i_6p6_get_int_status: 'canonical', // INT_STATUS bits\n    tile_sense_i_6p6_get_int_status2: 'canonical', // WOM bits\n    tile_sense_i_6p6_get_int_status3: 'canonical', // tilt/tap bits\n    tile_sense_i_6p6_int1_data_ready: 'canonical', // INT_SOURCE0\n    tile_sense_i_6p6_int1_fifo_ths: 'canonical',\n    tile_sense_i_6p6_int1_wom: 'canonical', // INT_SOURCE1\n    tile_sense_i_6p6_int2_data_ready: 'canonical', // INT_SOURCE3\n    tile_sense_i_6p6_int2_fifo_ths: 'canonical',\n    tile_sense_i_6p6_int2_wom: 'canonical', // INT_SOURCE4\n    tile_sense_i_6p6_wom_config: 'canonical', // thr = mg*256/1000\n\n    // hallucinated — placeholders not truly modeled\n    tile_sense_i_6p6_get_tap_result: 'hallucinated',\n    tile_sense_i_6p6_get_tap_result_flat: 'hallucinated',\n    tile_sense_i_6p6_wait_for_tap: 'hallucinated',\n    tile_sense_i_6p6_fifo_read_packets: 'hallucinated',\n    tile_sense_i_6p6_fifo_read_packets_flat: 'hallucinated',\n    tile_sense_i_6p6_set_filter_bw: 'hallucinated',\n    tile_sense_i_6p6_set_filter_order: 'hallucinated',\n    tile_sense_i_6p6_set_temp_filter: 'hallucinated',\n    tile_sense_i_6p6_set_gyro_offset: 'hallucinated',\n    tile_sense_i_6p6_set_accel_offset: 'hallucinated',\n    tile_sense_i_6p6_smd_config: 'hallucinated',\n    tile_sense_i_6p6_write_reg: 'hallucinated',\n    tile_sense_i_6p6_self_test: 'hallucinated',\n    power: 'canonical', // ICM-42686P Table 3 D.C. characteristics (LP figure estimated)\n  },\n\n  // Time-evolution: FIFO fills while enabled; the pedometer counts steps while\n  // the device is moving.\n  deriveState(state) {\n    const patch: Partial<State> = {};\n    if (state.fifo_mode !== 0 && sensorsOn(state)) {\n      patch.fifo_count = Math.min(4095, state.fifo_count + 1);\n    }\n    if (state.ped_enabled === 1 && motionMg(state) > 50) {\n      patch.step_count = state.step_count + 1;\n      patch.step_cadence = 8; // 2 steps/s in u6.2\n    } else if (state.ped_enabled === 1) {\n      patch.step_cadence = 0;\n    }\n    return patch;\n  },\n\n  // INT pin drive levels. Each pin ORs its routed sources; APEX tilt routes to\n  // INT1 by default. Active-low inverts the level.\n  padOutputs(state) {\n    const drdy = dataReady(state) === 1;\n    const fifo = fifoThsActive(state);\n    const wom = womActive(state);\n    const tilt = tiltActive(state);\n\n    let int1 =\n      (state.int1_drdy === 1 && drdy) ||\n      (state.int1_fifo === 1 && fifo) ||\n      (state.int1_wom === 1 && wom) ||\n      tilt;\n    let int2 =\n      (state.int2_drdy === 1 && drdy) ||\n      (state.int2_fifo === 1 && fifo) ||\n      (state.int2_wom === 1 && wom);\n\n    if (state.int_active_high === 0) {\n      int1 = !int1;\n      int2 = !int2;\n    }\n    return { INT1: int1 ? 1 : 0, INT2: int2 ? 1 : 0 };\n  },\n\n  // Single 1.8 V supply. Current by power mode (ICM-42686P datasheet typicals):\n  // sleep 7.5 µA, accel-only LN 0.28 mA, gyro-only LN 0.58 mA, both LN 0.70 mA.\n  power(state) {\n    const accelLN = state.accel_pm === 3;\n    const accelLP = state.accel_pm === 2;\n    const gyroLN = state.gyro_pm === 3;\n    let ua = 7.5; // sleep\n    if (gyroLN && (accelLN || accelLP)) ua = 700;\n    else if (gyroLN) ua = 580;\n    else if (accelLN) ua = 280;\n    else if (accelLP) ua = 20; // LP not in datasheet — estimate\n    return {\n      draw_ua: ua,\n      rails: [{ name: 'V+', role: 'supply', v_mv: 1800, i_ua: ua, pads: ['10'] }],\n    };\n  },\n};\n\nexport default sim;\n",
    "status": "validated",
    "updated_at": "2026-06-21T09:30:24.655Z"
  },
  "config": {
    "int1": {
      "kind": "boolean",
      "group": "Sidebands",
      "label": "Use INT1 line",
      "binding": {
        "pad": "9",
        "kind": "output"
      },
      "default": false,
      "options": [
        {
          "value": false,
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "disabled",
              "register": "INT_CONFIG.INT1"
            }
          ]
        },
        {
          "value": true,
          "netlist": {
            "expects": [
              {
                "to": {
                  "kind": "matchFunction",
                  "function": "GPIO",
                  "capabilities": [
                    "EXTI"
                  ]
                },
                "tag": "int9.attached",
                "from": {
                  "pad": "9",
                  "kind": "tile"
                },
                "role": "interrupt"
              }
            ]
          },
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "enabled",
              "register": "INT_CONFIG.INT1"
            }
          ]
        }
      ]
    },
    "int2": {
      "kind": "boolean",
      "group": "Sidebands",
      "label": "Use INT2 line",
      "binding": {
        "pad": "8",
        "kind": "output"
      },
      "default": false,
      "options": [
        {
          "value": false,
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "disabled",
              "register": "INT_CONFIG.INT2"
            }
          ]
        },
        {
          "value": true,
          "netlist": {
            "expects": [
              {
                "to": {
                  "kind": "matchFunction",
                  "function": "GPIO",
                  "capabilities": [
                    "EXTI"
                  ]
                },
                "tag": "int8.attached",
                "from": {
                  "pad": "8",
                  "kind": "tile"
                },
                "role": "interrupt"
              }
            ]
          },
          "firmware_contract": [
            {
              "via": "i2c",
              "type": "register",
              "value": "enabled",
              "register": "INT_CONFIG.INT2"
            }
          ]
        }
      ]
    },
    "i2cAddress": {
      "kind": "select",
      "when": {
        "interfaceMode": "i2c"
      },
      "group": "Interfaces",
      "label": "I2C address",
      "binding": {
        "pad": "2",
        "kind": "strap",
        "states": {
          "low": "0x68",
          "high": "0x69 (forced)",
          "open": "0x69 (default, via internal PU)"
        }
      },
      "default": "default",
      "options": [
        {
          "label": "0x69 (default)",
          "value": "default",
          "states": {
            "2": "open"
          },
          "derived": {
            "i2c_address": "0x69"
          },
          "contributes": {
            "busAddress": {
              "bus": "i2c",
              "value": "0x69"
            }
          }
        },
        {
          "label": "0x68 (alternate)",
          "value": "alternate",
          "states": {
            "2": "low"
          },
          "derived": {
            "i2c_address": "0x68"
          },
          "netlist": {
            "requires": [
              {
                "to": {
                  "kind": "rail",
                  "rail": "GND"
                },
                "tag": "i2cAddress.alternate.connection",
                "from": {
                  "pad": "2",
                  "kind": "tile"
                },
                "role": "power"
              }
            ]
          },
          "contributes": {
            "busAddress": {
              "bus": "i2c",
              "value": "0x68"
            }
          }
        }
      ]
    },
    "interfaceMode": {
      "kind": "select",
      "group": "Interfaces",
      "label": "Interface mode",
      "binding": {
        "pad": "3",
        "kind": "strap",
        "states": {
          "low": "SPI",
          "high": "I2C/I3C (forced)",
          "open": "I2C/I3C (default, via internal PU)"
        }
      },
      "default": "i2c",
      "options": [
        {
          "label": "I2C",
          "value": "i2c",
          "states": {
            "3": "open"
          },
          "activates": {
            "interface": "I2C"
          }
        },
        {
          "label": "SPI",
          "value": "spi",
          "states": {
            "3": "low"
          },
          "activates": {
            "interface": "SPI"
          }
        },
        {
          "label": "I3C",
          "value": "i3c",
          "states": {
            "3": "open"
          },
          "netlist": {
            "assumes": [
              "I3C uses 2-wire bus, I2C-compatible startup"
            ]
          },
          "activates": {
            "interface": "I3C"
          }
        }
      ]
    }
  }
}