Tiny Tapeout · ttsky25a · project 258 · tt_um_jnw_wulffern

Two temperature sensors,
measured properly.

Five days on JNW-TEMP — the two sensors designed by NTNU TFE4188 students — from a live readout, through a climate chamber, to the noise floor of the silicon itself. Both sensors read within of each other at room temperature.

Sensors GR06 · GR07 Instruments Saleae Logic Pro 16 · TT demo board RP2350B Run Ideas Carsten Wulff Built & written by Claude Opus 5
02 / What you asked for

The brief, as it actually arrived

Nine requests over the day. Most arrived mid-task, which is why the shape of the tool kept changing.

01
Create a local interface where I can plot the period of output 0… figure out whether we could control the tiny tapeout board from python to create a closed loop… a live demo plot… and an FFT over long time to show the noise. Set the whole scope: PySide6 app, Logic 2 automation, host-driven GR06 stimulus.
02
You will need to sample at as high frequency as possible. Each period is about 1 µs, and the pulse is only a few tens of ns. Correct, and load-bearing — it forced transition-based parsing instead of sample-based, and top-rate captures.
03
App owns serial. Answer to my question about who drives the board. Made the closed loop possible.
04
put the scripts in meas/ directory Package lives at meas/src/jnwtemp.
05
automation: 127.0.0.1:10430   mcp: 127.0.0.1:10530 Unblocked everything — the Logic 2 automation server had been refusing connections until then.
06
We should add a trace of the temperature versus time. a button to record the temperature would be good, and a button to stop recording. Streaming CSV recorder, flushed per row.
07
The recorder should also have some provenance (time,date,user etc) maybe a sidecar file JSON sidecar — and it made the CSV comment banner redundant, which is what later let cicwave read the files.
08
…longer captures with saleae, maybe up to a second? … make sure the capture is easy to plot in cicwave. 3) commit, push Found and fixed a real incompatibility: our CSV would not parse in cicwave at all.
09
fix cicwave  ·  capture the temperature for both sensors for a while and compare MCP server ported to SDK 2.x; the 15-minute dual-sensor run behind slide 07.
03 / The setup

One loop, two instruments

The host both drives the chip and listens to it. GR07 free-runs, so listening is enough; GR06 emits nothing until its reset is toggled, so the host has to close the loop.

Host · macOS jnwtemp PySide6 + pyqtgraph acquisition thread calibrate · record TT demo board RP2350B core MicroPython REPL project clock 64 MHz reset burst on ui_in[0] tt_um_jnw_wulffern sky130 · index 258 GR07 free-running PWM GR06 reset-triggered pulse Saleae Logic Pro 16 250 MS/s · 1 ch 125 MS/s · 2 ch edge transitions USB serial 4.8 ms/cmd clk · ui_in[0] 6 µs/edge uo_out[0] → D0 uo_out[2] → D2 Logic 2 automation · gRPC 127.0.0.1:10430 · binary edge export The dashed leg closes the loop: the host reads back what its own stimulus produced.
Colour marks the sensor, here and everywhere after: GR07 GR06. Everything else on the page is grey.
04 / The circuits

Same physics, two ways of reading it out

Both sensors are a PTAT current charging a capacitor into a comparator. The current rises with absolute temperature, so the time to reach the threshold falls with it:

t = Vref·C / I(T),   I(T) = k·T·ln(N) / (q·R)

So t ∝ 1/T, and the rate r = 1/t is proportional to absolute temperature. The app works in the rate domain and fits r = a·TK + b — with one calibration point it fits the ideal PTAT line through the origin, which is the only honest model from a single observation.

GR07 · uo_out[0]

Free-running PWM

Nerheim, Batalle Largo & Sætermo. The comparator output is re-timed by a clocked register, which also resets the ramp — so it oscillates on its own and the period is the observable. Nothing to drive; just listen. Its resolution depends on the project clock, which is why we run it as fast as it goes.

GR06 · uo_out[2]

Reset-triggered pulse

Sbaffi, Jensen & Klemetsdal. Asserting ResetTemp06 on ui_in[0] shorts the capacitor; releasing it produces exactly one pulse whose width is the observable. Asynchronous — no clock quantization — but it yields one measurement per stimulus instead of a million per second.

Measured characteristics of both sensors
MeasuredGR07GR06
Observableperiodhigh pulse width
Value at ~23 °C
Frequency909.8 kHz141.3 kHz equivalent
Sensitivity3.08 kHz/K0.48 kHz/K
Events per 0.5 s capture
Host stimulus needednonereset burst
Quantization15.6 ns clocknone (async)
05 / Closing the loop

Two bottlenecks stood between “works” and “usable”

GR06 needs a reset pulse every few hundred microseconds. Naively that is impossible from a host; both fixes were about finding where the time was going.

Serial round-trip
730 ms → 4.8 ms

pyserial's read(n) blocks until n bytes arrive or the timeout expires, so a read(4096) that gets 20 bytes still costs a full timeout — every call. Polling in_waiting instead made the REPL 150× faster.

Reset edge
15 ms → 6 µs

tt.ui_in[0] = 1 goes through the SDK's Logic wrapper and costs ~15 ms per edge. The underlying machine.Pin costs ~6 µs — 2500× less. The burst runs on the RP2350 itself, concurrently with the capture.

The result: one asynchronous burst of ~4500 reset pulses runs during a capture, so a single 0.5 s window yields thousands of independent GR06 measurements instead of the handful that host round-trips would allow.

06 / The surprise

GR07's period only takes four values

A 1-second capture holds periods. They land on distinct values, in two clusters one 64 MHz clock period apart.

GR07 period, 1 s capture at 250 MS/s
The 16 ns gap between clusters is one project-clock period (15.6 ns); the 4 ns substructure inside each cluster is the Saleae's own sample quantum.

This slide used to call the retiming "accidentally a first-order sigma-delta", whose quantisation noise would rise toward Nyquist. It does not. Keeping every edge of a 1 s capture gives individual periods — a record reaching , about the bandwidth the demo board can reach — and the period noise is flat across all of it, varying in total. First-order shaping would climb four decades over that span. Three things agree it is white: the flat spectrum integrates to against measured directly, so it holds all the variance; a two-level dither at the measured fraction predicts ; and the accumulated phase falls as exactly 1/f, which is integrated white noise. The earlier chamber fit already implied this — it matched a model assuming independent quantisation per period at r = 0.997 — and I did not notice the contradiction. Nothing downstream changes: every noise budget here already assumed the white case. Only the explanation was wrong.

The comparator trip is re-timed by the project clock, so each period is either N or N+1 clock cycles. One whole step is . All the resolution therefore comes from the ratio between the two levels — the circuit dithers, and averaging ~900 000 periods reads that ratio to a few millikelvin.

This is also why outlier rejection had to be rebuilt — see slide 11.

07 / The comparison

Both sensors, , same time base

Captured simultaneously on two channels — identical thermal environment, identical clock. Each is calibrated at a single point, 23.00 °C, on its first reading; everything after that is the silicon talking.

GR07 GR06 hover to read values
One point per 0.5 s capture. The two traces wander independently — see the reading below the chart. The table further down is the same data numerically.
Correlation

Between the two temperature series — and it is the headline result. See below.

Offset

Mean GR06 − GR07, after single-point calibration of each.

Precision ratio

GR07's per-reading noise advantage, from ~200× more events.

GR07 GR06 Allan deviation — lower is better
How far averaging actually helps. A falling curve means more averaging buys precision; a rising one means drift has taken over and longer averaging makes the answer worse.
Per reading (0.5 s capture)GR07GR06
Mean temperature
Statistical precision (SEM)
Reading-to-reading spread
Peak-to-peak over the run
Per-event σ
Allan deviation @ ~10 s
Events rejected as glitches

08 / Provoking it

A blast of freeze spray, then a fingertip

Quiet-room data only shows a sensor's noise. To see what it does with a real thermal input: a can of freeze spray at , then a finger held on the package from 88 s to 122 s. Both sensors, one capture, 1 ms points.

GR07 GR06 gaps are dead time between captures
The whole run. Vertical marks are where the derivative says something happened; the trace between captures is genuinely absent, not smoothed.

A caveat on the absolute numbers. Both sensors are calibrated at a single point, 23 °C, against the ideal PTAT line through the origin — there is no offset term to absorb comparator delay or reset time. At 8 °C that model is extrapolating 15 K beyond its only anchor, so the depth carries a systematic uncertainty we cannot bound without a second calibration temperature. The rates and the timing are on much firmer ground: they depend on the slope of the trace, not on where the zero sits.

And what is measured is the die, not the spray. The can is rated −55 °C, but the sensor only ever sees its own silicon, reached through the package: what the −37 K/s describes is how fast the die followed, not how cold the propellant was.

09 / What it settles

A real event moves both sensors. Noise doesn't.

Slide 07 found the two sensors uncorrelated in a quiet room and concluded the wander was sensor-local rather than ambient. This run is the control that decides it.

PhaseGR07 vs GR06 correlationWhat is happening

When the die genuinely changes temperature the two sensors agree almost perfectly, despite different circuits, different readout schemes and a 200× difference in events per point. When nothing is happening to the die they drift apart. That is exactly the signature that the quiet-run wander was not temperature — and it means these sensors are far better at telling you that something changed than at telling you what the temperature is.

The cost of re-timing: GR07 has dead zones

The stimulus run swept GR07 across 15 K, and its noise turned out to depend strongly on where it sat. The period is re-timed by the 64 MHz clock, so what matters is where it falls between two clock edges:

TemperaturePeriod, in clock cycles Distance to a whole cycleNoise per 1 ms point

The noise collapses exactly where the period lands on a whole number of clock cycles, and peaks half-way between. That is a dead zone, not precision: when the ramp always crosses in the same clock interval every period gives the same answer, the dither that carried the resolution switches off, and the sensor stops responding to small changes while looking ten times quieter. It is the same mechanism that makes a sigma-delta idle — and it explains slide 07's rising Allan curve, since idle-tone plateaus read as drift.

Practical consequence: a quiet-looking GR07 reading is not evidence of a good one. GR06, having no clock in its path, has no equivalent failure.

10 / A gain difference that isn't one

Breathing on the chip, GR06 swings 2.4× further

Four breaths over . Each moves both sensors, but not by the same amount: GR06 reads about × the excursion GR07 does. The obvious reading is that the two circuits have different gains. They don't.

GR07 GR06 four breaths

The same two sensors agree to within over the 15 K freeze-spray run. A real difference in calibrated gain would show up more strongly over a wider excursion, not vanish. So this is a property of small, fast signals, not of the circuits' sensitivity.

It is GR07 sitting on a clock edge

Slide 09 showed GR07's period lands on a staircase of whole 64 MHz clock cycles, and that its dither — and with it its resolution — collapses wherever the period sits close to a whole cycle. This run happened to sit exactly there, and stayed there: a 1 K breath moves GR07 by about a quarter of a clock cycle, nowhere near enough to escape.

During the runGR07Period (clocks) To nearest whole cycleGR07 noiseGR06 noise

GR07's noise falls to 9 mK — not because it got better, but because it stopped responding. GR06, with no clock anywhere in its path, sits at a flat ~110 mK throughout. The sensor that looks ten times quieter is the one compressing the signal.

The simulations agree the gains should match

Worth checking against what the students designed, because a real 2.4× mismatch would be a design finding rather than a measurement artefact. Fitting each simulated transfer to t = K/T + td — a PTAT ramp plus whatever fixed time does not scale with temperature — gives a fixed term of only −2 % of the period for GR06 and −5 % for GR07. Both are near-ideal, so their gains should differ by a few per cent, not a factor of two. GR07's simulated PTAT current is proportional to absolute temperature within 2 % across corners (13.8 / 18.2 / 24.9 µA cold / typical / hot).

Measurement, simulation and the 15 K run all say the same thing: the factor of two is an artefact of reading a quantized sensor over an excursion smaller than its quantum.

What to do about it. For small excursions, trust GR06. For GR07, either move it off the edge — the project clock sets where the staircase falls, so changing the clock frequency shifts the dead zones — or accept that excursions of a few kelvin are compressed by an unknown factor between one and about three.

11 / Against a real reference

A climate chamber, , and the first honest transfer curve

Everything up to here calibrated each sensor against itself at one room-temperature point, so it could measure change but never accuracy. A Vötsch chamber stepping through set points, logging its own probe beside both sensors, finally supplies an outside opinion. Each point below is the mean of the last s of a dwell, after the oven and the die have both stopped moving.

GR07 GR06 residual after the best straight line — lower is better
Integral nonlinearity, in kelvin so the two are comparable despite rates that differ sixfold.
GR07 own GR06 own dashed: the part both sensors agree on
Once the shared component is removed both sensors are about twice as linear as the raw curve suggests, and — tellingly — they are equally linear.
GR07 slope

Rate per kelvin. A PTAT current into a fixed capacitor, so the rate should be proportional to absolute temperature — and it is, to within the curve above.

GR06 slope

Six times lower, because GR06 is read once per reset rather than free-running. Fewer events per capture, but no clock anywhere near it.

GR07 quantisation

One 64 MHz clock cycle, in kelvin. Measured across fully-traversed codes; worst DNL LSB.

12 / What calibration buys

One point, two points, three points

The question a product asks is not "how linear is it" but "how many oven visits must I pay for". Nothing here fits more than three points: fitting all fourteen chamber set points measures how well a polynomial can chase this part, which is not a calibration anybody performs and flatters the sensor.

GR07 GR06 reading − reference, shared scale
CalibrationGR07 worst error GR06 worst errorCost

GR06 behaves the way a sensor should — each extra trim point buys accuracy, down to over 5–70 °C. GR07 gets worse from one point to two. A two-point fit only corrects a slope, and GR07's error is not a slope: its residual changes sign across the range, so anchoring the line at 20 and 60 °C tilts it to satisfy two points that happen to sit on opposite excursions and throws the middle further out. More trim points only help a curve they can actually follow.

An earlier version of this deck blamed GR07's floor on its quantiser. That was wrong. Its period is re-timed onto whole clock cycles, but it sits at some fraction between two codes and alternates between them — so the quantiser is dithered, and a dithered quantiser's mean survives averaging. The model for that (σ = √(p(1−p))·LSB⁄√N) predicts the measured per-bin noise with r = across all 14 setpoints, and what is left in a dwell mean is — some three orders of magnitude below the error it was supposed to explain. Quantisation sets GR07's single-shot noise, not its accuracy. A faster clock would give a quieter live trace and nothing else.

13 / The trap

GR06 does not drift — it switches

Ninety seconds of GR06 at a fixed , with the dwell drift removed. If this were thermal noise it would be a fuzzy band around zero. Instead it sits flat, drops to a second flat level, stays there for a second or two, and comes back. That is random telegraph noise: one charge trap in the silicon capturing and emitting a single carrier.

GR06, 0.3 s median two levels, not one distribution
The histogram on the right of the trace is the same data seen end-on. A single noise process gives one hump; this has two.
Step size

How far one trapped carrier moves the reading. Flat from 5 to 55 °C — it is one trap, not a population of them.

Lifetime

Mean time in the low state, cold to hot. Thermally activated: , which is what identifies it as a trap rather than anything in the instrument.

Occupancy

Fraction of the time spent in the low state. Not a rare glitch — at some temperatures it is where the sensor lives half its life.

mean burst lifetime log scale — a straight line here means Arrhenius
Above about 55 °C the two levels merge into the noise and the fit stops being meaningful, so those points are drawn hollow and excluded.
Why it matters

GR06's ±0.35 K three-point calibration is a ninety-second-mean number. A single reading can be off whenever the trap is occupied. That, not linearity, is GR06's real accuracy limit — and unlike GR07's quantisation it is cheap to fix, because a median over a second or two removes it. The dark line on the trace above is that filter.

How this was nearly got wrong

The natural move is to reach for a PSD, and it gives a confident false answer. Detrending with a 5 s Savitzky–Golay filter is a 0.2 Hz high-pass, and the filter's own corner appears as a beautiful Lorentzian peak — fitting to 298 meV with r = −0.96. The tell was that GR07 showed the same peak while being incoherent with GR06, and two independent sensors cannot share a noise process and be incoherent. RTS is a time-domain claim; it has to be settled in the time domain.

14 / The noise floor

What is left when nothing is happening

Half an hour per sensor at a fixed temperature, measured by the demo board alone. The logic analyser is the wrong instrument for this: its capture–arm–export cycle leaves dead time between blocks, and dead time is poison for a spectrum. The RP2350 counts continuously — duty, the rest being the USB transfer — so the record spans in one piece.

GR06 GR07 dashed: what the instrument itself contributes
GR06 white floor

Flat above ~50 Hz and clear of the instrument, so this is the sensor and not the counter.

kT/C on the real capacitor

One cap_mim_m3_1 at 5×5 µm is from the PDK, and the comparator trips at . That accounts for only of the white power.

Shared with GR07

Upper limit from the cross-correlation — see below. Both sensors reference VDD, so supply noise would have to appear in both.

GR06 GR07 τ−1/2 is what white noise does
Ruling out the supply

GR06's comparator has no reference net — only VDD and VSS — so its trip point rides the supply and dt/t = dVDD/VDD exactly. GR07 is the same architecture, so its frequency moves the opposite way. Supply noise therefore has to appear in both, anti-phase, at unity gain. Measuring both at once for and cross-correlating gives a coherence of against an estimator bias of — nothing, and the same nothing in a time-shifted null and in a control where the two were never sampled together.

So what is it?

A null is only useful as a number. The shared term is under on a 1.8 V rail, against the it would need if GR06's noise were supply-borne — excluded by . Not the supply, and not kT/C. What is left is GR06's own comparator, which is also where the trap on the previous slide lives. The practical consequence: growing the ramp capacitor is not the lever, because kT/C is not what limits it.

15 / Every edge

What the logic analyser adds, and where it runs out

The demo board reduces as it measures — one value per 907 periods — so its spectrum stops at 500 Hz. Keeping every edge of a one-second capture gives individual periods, a record reaching : about the bandwidth, and the only view of what the retiming does between one period and the next.

GR07 measured dashed: what a first-order Δ∑ would look like
A second opinion on GR06

The Saleae's sample against the board's 15.6 ns tick measures the same noise with a lower instrument floor. On the white floor — the one quantity that does not depend on how long you watch — they agree to :

InstrumentfloorGR06 white floor

Comparing total noise instead would be wrong: the board's is a 32-minute integral containing the 1/f and the trap, and a one-second capture cannot hold the same low-frequency content. Those two once agreed to 0.2 %, which was luck.

Still open

There is structure near 250–450 kHz that is not explained. Part of it moves with the analyser's sample rate ( against ), so that part is the instrument. Part appears at both rates, so that part is in the signal. Both sensors reference VDD, which makes the supply the natural suspect — but the feature's amplitude swings about for a 0.1 % change in the sensor's own frequency, and no external tone behaves like that. It looks like quantiser idle tones. Settling it needs a wide temperature sweep, where an alias moves linearly and an idle tone moves as a wrapping sawtooth. It is narrowband, and no number in this deck depends on it.

16 / The campaign

Scripts, data, instruments

Five days, 31 July to 4 August, from "can we see the period of output 0" to a pair of sensors characterised against an external reference and down to their own noise floor. What follows is the inventory, so the next person knows what exists and what it cost to make.

WhenWhat it added
31 Jul Live readout, both sensors from one capture, millisecond trace, recording with provenance. Freeze spray and a fingertip as the first stimulus.
1 Aug Survive a power cycle — the project clock came back at 0, which reads as a dead sensor rather than a clock fault.
3 Aug Measure with the demo board alone, no logic analyser. Drive the climate chamber and sweep 5–70 °C: the first external reference, and the first honest transfer curve.
4 Aug Settle sweeps on drift rather than setpoint. Retract the quantisation claim. Find the real clock ceiling. Fix the GUI stall. Long noise runs, and the cross-correlation that rules out the supply.
Instruments

Three, all driven from Python

Saleae Logic Pro 16 over the Logic 2 automation API on port 10430. Sample rate is negotiated with the device rather than assumed — it does 250 MS/s on one channel, 125 on two, and the first version of this hard-coded 500 and was quietly wrong.

TinyTapeout RP2350B demo board over a MicroPython raw REPL, which selects project 258, sets the 64 MHz project clock, and pulses ui_in[0] to trigger GR06.

Vötsch climate chamber over ASCII-2 on Ethernet, stepping 5 → 70 °C and logging its own probe alongside the sensors.

Software

A live instrument, not a script

meas/src/jnwtemp — PySide6 readout with sensor select, dual capture, calibration, a streaming recorder, and plotting handed to cicwave so the keymap matches every other waveform tool here.

meas/scripts — the analysis half: chamber_extract reduces a run to committable form, chamber_analyse and chamber_figures produce the numbers and the plots, chamber_burst finds the trap, chamber_deck and build_presentation put them in these slides. Every figure here regenerates from the repo alone.

Data

Reduced, not truncated

The chamber run is 82 MB of 10 ms samples, most of it the oven slewing — and slewing data is unusable anyway, because the chamber's probe does not represent the die while the loop is moving.

What is committed is the settled tail of each dwell: 119 810 samples, 5.5 MB, reproducing every number in these slides to under 30 µK. Rates are stored to 0.1 Hz — 1 600× below GR07's own per-sample noise, so the digits dropped were noise, and noise is what compresses worst. Binary was measured and rejected: git already compresses objects, so parquet saved 80 kB in the repo and cost a dependency.

What the day actually established

Both sensors work. GR06 reaches over 65 K with three trim points and is the better part despite being the slower, quieter one. GR07 lands near — but not because of its clock: once the reference error shared with GR06 is removed the two are equally linear, at 0.79 K each. What the clock does set is GR07's single-shot noise, and that is also what made its earlier "gain difference" against GR06 an artefact rather than a design error. GR06's own floor is not thermal noise but a single charge trap worth half a kelvin.

What it cost to find out

Four wrong answers that looked right: an outlier filter that deleted 49 % of GR07's events and returned a plausible temperature; a hard-coded sample rate; a settling estimate from a reference that only reports every 2 s; and a dwell extract averaging a different window than the analysis, off by 360 mK. Each was caught by checking a number against a second, independent route to it.

17 / What I got wrong

Four corrections worth recording

Three were mine. The measurement one mattered most, because it was silently producing plausible numbers.

Measurement · silent

Outlier rejection was deleting the signal

A MAD/σ rule reads GR07's far cluster as outliers. One recorded row shows 44 924 of 91 512 events discarded and the temperature biased to 24.17 °C. Rejection is now by ratio to the median (±40 %), which catches what actually goes wrong — a missed edge doubles the interval — and is blind to legitimate dither. After the fix: zero rejected, and the reading returned to ~22.8 °C.

Assumption

The Logic Pro does not do 500 MS/s here

I hardcoded a rate/channel table from memory. The device caps at 250 MS/s on one channel and 125 MS/s on two. Rates are now negotiated from the device's own rejection message rather than assumed.

Performance

Plotting 180 000 points froze the GUI

The Qt main thread stalled inside PlotCurveItem.paint; pyqtgraph's autoDownsample still paints the full path. Now decimated to 4 000 points with a min/max envelope, so outliers survive the thinning.

Ecosystem

cicwave could not read our files — then could not start

The # provenance banner made the CSV unparseable, and the MCP plot tool has no way to pass --csv-comment. Dropping the banner fixed it. Separately cicwave-mcp died at import under MCP SDK 2.x, which renamed FastMCP to MCPServer — now supports both generations.

18 / Shipped

What exists now

meas/src/jnwtemp

Live readout: sensor select, channel detection, one- and two-point calibration, three plot modes (in-capture noise, long-term drift, Allan deviation), streaming recorder and per-event capture export. 13 modules, ~3 100 lines. Committed to jnw-tt-2025@c0830cb.

cicwave, SDK 2.x

MCP server ported and verified over a real stdio transport — initialize, list_tools, inline PNGs, text analysis. Full suite 160 passed. Committed to cicwave@eb823a6.

Data that outlives the session

Every recording writes a JSON sidecar: timestamps, operator, software and git commit, DUT and pin configuration, instrument identities, and every calibration point. Data files stay flag-free so any tool opens them.

Known limits

Single-point calibration assumes ideal PTAT through the origin — a second temperature would absorb comparator delay. And cicwave still builds Qt widgets off the main thread; it works, but it is undefined behaviour.

19 / Running it yourself

From a bare board to a live trace

Two USB cables, three probe clips and one calibration press. The whole thing takes about five minutes, and most of the ways it goes wrong are listed at the bottom.

Wire it

The demo board and the logic analyser both go to the host over USB. The analyser needs three clips onto the demo board header:

SaleaeDemo boardCarries
D0uo_out[0] GR07, free-running
D2uo_out[2] GR06, reset-triggered
GNDGND required — a floating ground reads noise

Any channel works; the app has a Detect active channels button that reports which ones carry a signal, so the wiring is measured rather than guessed. Threshold stays at 1.2 V.

Install and start

cd meas
pip install -r requirements.txt
python3 main.py

Two things must be true before Connect will work:

1. In Logic 2, Preferences → Enable automation server (port 10430). Without it the app cannot capture at all, and nothing else will make it work.

2. Nothing else may hold the board's serial port. macOS reports it busy while a Chrome tab has it over WebSerial, so close TT Commander first.

First run

Connect Top right. The three chips go green: Logic 2, demo board, project. The app selects project 258 and sets the clock to 64 MHz itself.
Pick a sensor GR07 free-runs and needs nothing driving it. GR06 emits nothing without the reset burst, so it needs the demo board connected. Both captures the two together in one capture — the only way to compare them meaningfully.
Start The trace appears within a second or two. Nothing is in degrees yet — the tile says so.
Calibrate here Set Reference to the true ambient and press it. That one press fits both sensors if you are in Both mode. Degrees appear immediately; the history resets, because earlier points were computed with the old model.
Record to CSV… Streams one row per trace bin, flushed every capture, with a .meta.json sidecar carrying the calibration and the instrument state. Roughly 1.4 MB/min at 1 ms bins. It also starts the loop if you had it paused.

Reading the trace

The temperature pane is cicwave's waveform plot, so its keys work here too. Click the plot first, then:

Cursors A and B print their values and the delta between them under the plot, alongside min / max / mean / σ / rms / peak-to-peak per series. The view stops auto-fitting the moment you zoom, so it will not re-frame itself while you are reading a value off; f hands it back.

Three settings that matter

Capture

Each capture costs a fixed ~0.7 s of overhead, so longer is strictly better for throughput: 0.5 s gives 41 % duty, 2 s gives 71 %. You pay only in how often the display updates. Below 0.2 s is pointless.

Trace bin

Time resolution of the trace. 1 ms suits GR07 (~910 events per point). GR06 fires only ~4500 times a second, so 1 ms leaves 4 events per point — the panel warns, and ~20 ms fixes it.

Setup / wiring

Collapsed by default and rarely needed: channel, sample rate (which is negotiated with the device anyway), threshold, project clock and the GR06 reset timing. Bottom plot switches the lower pane between the two noise spectra, the Allan deviation and the raw per-event trace — two panes, so the temperature trace has room to be read.

After a power cycle

The demo board keeps its project selection across a reset but not its clock: tt.auto_clocking_freq comes back 0, and GR07 — whose comparator output is re-timed by that clock — simply stops toggling. GR06 is unaffected, which makes it look like a wiring fault on one channel. Pressing Connect fixes it; the app sets the clock as part of configuring the chip, and warns in the log if it fails to start.

Logic 2 is the other half: an application update resets the Enable automation server preference, so it has to be ticked again. The two failures look identical from the outside — nothing works — which is why the app now connects the two instruments independently and reports each one separately rather than giving up at the first.

If it misbehaves

SymptomCause
Nothing works after a restart clock stopped and/or automation server off — see below
Connect fails on Logic 2 automation server not enabled (a Logic 2 update resets it)
GR07 flat, GR06 fine project clock stopped; Connect restarts it
Demo board “unavailable: could not open …” a Chrome/Commander tab holds the serial port
GR06 reads nothing no demo board — it needs the reset burst
Trace looks like pure noise bin too small for the sensor; widen it
Temperature is stable but wrong one-point calibration; add a second temperature
Sample rate is lower than requested expected — negotiated down for the channel count