The I²C timing bug we caught four weeks before fab

Adam Szefer · Co-founder & CTOUpdated October 22, 2026

Prev. Bloomberg · Gyfted. Systems and simulation infrastructure — the engine that runs your firmware against the circuit.

On the homepage we show an I²C check failing: SCL t_HIGH measured at 0.42 µs against the 0.60 µs Fast-mode minimum. This is the longer version: what broke, why it first looked like clock stretching, and how the check runs in CI.

The symptom we were aiming to prevent

The firmware ran the MCU's I²C controller in Fast-mode (400 kHz), with timing values carried over from a board with a different peripheral clock. On a physical spin that kind of mismatch shows up as intermittent NACKs from one sensor and “works if I lower the bus speed.” On a calendar it shows up as a respin or weeks of bench time.

Why it looked like clock stretching, and wasn't

The sensor on that bus stretches the clock during burst reads, so stretching was the first suspect. But a target can only hold SCL low. It can never make the high phase shorter. A short t_HIGH always comes from the controller, here from a timing register computed for the wrong clock.

Why the eval kit didn't show it

The evaluation module ran at a different clock, and most parts tolerate a slightly short high phase on a quiet bench. Register-level stubs never look at bus timing at all. If your simulation always ACKs on time, you are testing a board you are not building.

What the failing check looked like

We ran unmodified firmware against a netlist-derived simulation of the board. The check measured SCL high time from the pin edges during the burst read. When t_HIGH dropped below the Fast-mode floor, the run failed with a traceable message: the same thing you would chase on a scope, available before copper.

Failure summary (simplified from the partner run)
FAIL  i2c1  t_HIGH
  measured  0.42 µs
  required  ≥ 0.60 µs (Fast-mode)
  context   burst read at 400 kHz
  firmware  payload.elf @ bring-up suite
  delta     4 weeks before scheduled fab

What we changed before fab

The I²C timing register was recomputed for the board's real peripheral clock. The check stayed in CI, so a later clock-tree change cannot bring the bug back silently. The first boards still needed bench time, for sensor accuracy rather than for rediscovering bus timing.

Takeaway for embedded teams

If your risk is hardware/firmware integration, demand that pre-silicon simulation can disagree with you. Green bars from always-polite peripherals are not safety. Encode the datasheet limits you already worry about as tests, and run them on the topology in your netlist.

HardLabs

Send us your design. We'll send you a simulator.

We build a board simulator from your netlist, schematic, and BOM, run your real firmware against it, and fail CI when pinmux, buses, or timing contracts break.

  • A working simulator of your board, built from netlist, schematic, and BOM.
  • Peripheral models attached so your firmware can talk to the board.
  • A setup that runs your real firmware binary — no rewrite required.

Pilot details · App

Related

HardLabsAll blog