All papers
EssayComputePhysicsMaterialsEngineering

TEMPO: A Time-Domain Ferroelectric Compute-in-Memory Macro with Drift-Cancelling Twin Cells and Significance-Ordered Early Termination

Oleksii Vergunenko

Published August 30, 2026· 13 min read

The energy cost of moving data between memory and processing units now dominates the energy cost of arithmetic itself, often by two to three orders of magnitude for off-chip access. Compute-in-memory (CIM) architectures attack this bottleneck by performing multiply-accumulate (MAC) operations inside the memory array, but analog CIM designs have been persistently limited by three coupled problems: conductance drift in the storage devices, the power and area cost of high-resolution analog-to-digital conversion, and the mismatch between fixed-precision readout and the highly non-uniform significance of bits in real workloads. We present TEMPO (Time-domain Elastic-precision Memory-based Parallel Operator), a compute-in-memory macro design based on multi-level ferroelectric FETs (FeFETs) that addresses all three problems through co-design rather than isolated fixes. TEMPO contributes: (1) a twin-cell differential encoding in which each weight is stored as the difference of two co-located FeFET threshold states, cancelling drift, temperature sensitivity, and array-gradient nonidealities to first order; (2) a current-controlled-oscillator (CCO) time-domain readout that replaces power-hungry SAR ADCs with counters, giving linear digitization whose resolution can be traded against integration time cycle-by-cycle; and (3) significance-ordered early termination, which processes input activations most-significant-bit-plane first and terminates accumulation when the remaining bit-planes provably cannot change the quantized output, converting workload sparsity and value distribution directly into energy savings. Our analytical energy model, calibrated against published FeFET device measurements and fabricated CIM macro reports, projects 41 TOPS/W (INT8-equivalent, dense) rising to 97 TOPS/W on ResNet-50 activation distributions with early termination enabled, at an estimated accuracy loss of <0.4% top-1 relative to an INT8 digital baseline in behavioral simulation. We identify FeFET endurance and read-disturb accumulation as the binding open risks and state the measurements that would falsify the design's viability.

An essay. A thesis put forward by its author — an argument, specification, or design — and not a verified result.

1. Introduction

For four decades the implicit contract of the von Neumann architecture was that arithmetic was expensive and communication was cheap. That contract has inverted. In modern process nodes, a 32-bit floating-point multiply costs on the order of a picojoule, while fetching the same 32 bits from off-chip DRAM costs on the order of a nanojoule — a gap of roughly three orders of magnitude [1, 2]. For workloads whose arithmetic intensity is low and whose working sets are large — dominated today by deep neural network (DNN) inference — the memory system, not the arithmetic units, sets the energy floor.

Compute-in-memory proposes a structural rather than incremental response: perform the dominant kernel (matrix-vector multiplication) inside the memory array itself, so that weights never move. The physics is cooperative. A resistive crossbar performs an analog MAC "for free": applying voltages to rows and summing currents on columns computes a vector-matrix product via Ohm's law and Kirchhoff's current law in a single step [3, 4]. This observation has produced a decade of architectures — ISAAC [3], PRIME [4], and a long line of SRAM- and ReRAM-based macros reported at ISSCC and VLSI — yet analog CIM remains largely absent from production silicon. The reasons are not mysterious, and they are not primarily about peak efficiency. They are about error structure and readout cost:

  • Device instability. Analog storage devices (ReRAM, PCM, FeFET) drift: their conductance or threshold voltage shifts with time, temperature, and read/write history. A weight that was correct at programming time is a distribution, not a value, at inference time [5].
  • ADC dominance. In reported analog macros, the column ADCs frequently consume 60–85% of total macro energy and a comparable fraction of area [3, 6]. The array computes cheaply; digitizing the answer does not.
  • Precision rigidity. Conventional readout digitizes every column to fixed resolution on every cycle, regardless of whether the workload needed it. Real activation and weight distributions are heavily concentrated near zero; most conversion energy is spent resolving bits that do not affect the quantized output.

The position of this paper is that these three problems should be treated as one co-design problem, because the same architectural choices touch all of them. TEMPO is our proposed instantiation. Section 2 reviews the relevant background. Sections 3–5 present the design. Section 6 develops the analytical energy model, Section 7 the behavioral evaluation, Section 8 the limitations, and Section 9 the claims ledger.

2. Background and Related Work

2.1 The CIM design space

CIM designs occupy a spectrum from digital to analog. Digital CIM (bitwise operations in SRAM, e.g., Ambit's bulk in-DRAM AND/OR [7], and digital MAC SRAM macros) preserves exactness but caps the density and parallelism benefit. Analog CIM maximizes parallelism — one array activation computes hundreds of MACs — but imports the full error budget of analog signal chains. Charge-domain SRAM CIM (capacitive coupling onto a shared readout line) [6] sits between: linear and reasonably stable, but weight storage remains volatile and 6T–8T cell area bounds density.

2.2 Storage device options

Three nonvolatile device families dominate analog CIM proposals. ReRAM offers small cells and multi-level conductance but suffers from cycle-to-cycle programming stochasticity and conductance drift [5]. PCM has mature multi-level operation but pronounced resistance drift with a well-characterized power-law time dependence. FeFETs — transistors with a ferroelectric (typically HfO₂-based) gate layer — store state as a threshold-voltage (V_T) shift [8]. FeFETs are attractive for CIM because the read path is a transistor channel (high on/off ratio, low read current), programming is field-driven (no large programming currents), and the device is CMOS-compatible in scaled nodes. Their principal open weaknesses are endurance (reported values range from ~10⁵ to ~10¹⁰ cycles depending on stack and conditions [8]) and V_T instability from charge trapping.

TEMPO adopts FeFETs, storing 2 bits per device (four V_T levels), and treats device instability not as a parameter to be minimized at the device level but as a common-mode signal to be cancelled at the circuit level (Section 3).

2.3 Time-domain readout

The insight that digitization can happen in the time domain rather than the voltage domain is established in the ADC literature and has been applied to CIM in scattered form: current-controlled oscillators (CCOs) convert a column current into a frequency, and a counter over a fixed window yields a digital code linear in current. Counters and phase accumulators scale gracefully with CMOS, unlike comparator-ladder or SAR structures whose energy grows steeply with resolution. TEMPO's contribution is not the CCO itself but its co-design with bit-plane-serial input delivery, which makes conversion resolution elastic per cycle (Section 5).

3. Twin-Cell Differential Weight Encoding

3.1 The error model

Let the programmed conductance-equivalent read current of a FeFET cell at gate overdrive V_read be I(w, t, T, h), a function of the stored level w, time t, temperature T, and disturb history h. Empirically, the dominant instability terms are: (a) a slow retention drift common to cells programmed under identical conditions in the same array neighborhood; (b) a temperature coefficient largely level-independent to first order; and (c) array-level gradients (fabrication and IR-drop) that vary smoothly across the array. Formally, decompose:

I = I_ideal(w) + δ_common(t, T, x, y) + δ_cell

where δ_common captures (a)–(c) as a smooth field over array position (x, y) and δ_cell is the residual per-cell idiosyncratic error.

3.2 The encoding

TEMPO stores each signed weight as a twin cell: two physically adjacent FeFETs, F⁺ and F⁻, programmed such that the weight is represented by the difference of their levels, w ∝ L⁺ − L⁻. The two devices share the same wordline, the same local temperature, the same array-position drift field, and (by symmetric layout) the same IR-drop environment. Their currents are steered to complementary accumulation lines, and the MAC result is formed as the difference of the two accumulated quantities.

Because δ_common is, by construction, common to both cells of a pair, it cancels in the difference:

I⁺ − I⁻ = [I_ideal(L⁺) − I_ideal(L⁻)] + (δ⁺_cell − δ⁻_cell)

The systematic error terms — retention drift, temperature, gradients — drop out to first order, leaving only the idiosyncratic per-cell residual, which is zero-mean and, across an N-element dot product, attenuates statistically as √N relative to the signal's N-scaling.

3.3 What this costs and what it buys

The cost is explicit: 2× cell count per weight, plus a differential (rather than single-ended) readout chain. The purchase is that the hardest error terms — the ones that are correlated, time-varying, and impossible to calibrate out once at deployment — become common-mode. Single-ended designs must chase these with periodic recalibration, temperature compensation tables, or frequent reprogramming (spending endurance). Differential encoding converts a calibration-and-maintenance problem into an area problem, and area is the currency FeFET density can afford to spend. This is a deliberate trade of a cheap resource against an expensive failure mode, and it is the design's central bet.

A second-order benefit: signed weights are represented natively (no offset-binary column and subtraction pass, as in many ReRAM schemes), halving array activations for signed layers relative to dual-array positive/negative decompositions.

4. Array Organization and Time-Domain MAC

4.1 Macro organization

The TEMPO macro is a 256×256 twin-cell array (256 rows of inputs, 256 output columns, 2 FeFETs per weight, 131,072 devices per tile), organized as follows:

  • Rows carry bit-plane-serial input activations: an 8-bit activation is delivered as eight one-bit wordline pulses, most significant bit-plane first (Section 5).
  • Columns are differential pairs of accumulation lines (A⁺, A⁻), each terminated in a current-controlled oscillator.
  • Readout per column consists of two CCOs and an up/down counter: the counter increments at the frequency of CCO⁺ and decrements at the frequency of CCO⁻. At the end of the integration window, the counter holds a digital value proportional to Σᵢ aᵢ(I⁺ᵢ − I⁻ᵢ), i.e., the signed dot product — digitized without any voltage-domain ADC.

4.2 Linearity

The CCO's frequency is designed to be linear in injected current over the operating range (a standard current-starved ring oscillator biased in its linear region). Counting cycles over a window of duration τ yields a code N = f(I)·τ ≈ k·I·τ. Deviations from CCO linearity are static and per-column, so they are absorbed into a one-time per-column two-point calibration — unlike device drift, CCO nonlinearity does not walk over time at the rates FeFET states do, and the differential counter structure additionally cancels CCO common-mode sensitivity to supply and temperature between the paired oscillators.

4.3 Elastic resolution

The counter's effective resolution is set by the integration window τ: doubling τ adds one bit. This is the property Section 5 exploits — resolution becomes a runtime scheduling decision, not a hardware constant. A SAR ADC's energy is committed the moment conversion begins; a counter's energy is proportional to how long you choose to listen.

5. Significance-Ordered Early Termination (SOET)

5.1 Bit-plane-serial input, MSB first

Activations are delivered one bit-plane per array cycle, MSB first. After bit-plane b, the accumulated counter state equals the partial dot product using the top (8−b) activation bits, and the maximum possible contribution of all remaining bit-planes is analytically bounded: it is at most (2^b − 1)/2⁸ of the maximum per-plane contribution, scaled by the column's L1 weight mass, which is a compile-time constant per column.

5.2 The termination test

The downstream consumer of a CIM column is almost never the raw high-resolution MAC; it is a quantized value — typically an INT8 activation after scale, bias, and ReLU. TEMPO's per-column termination logic maintains the running counter value C_b and the precomputed remaining-mass bound R_b, and terminates the column when the interval [C_b − R_b, C_b + R_b] maps to a single post-quantization output code — or, in the common ReLU case, when C_b + R_b < 0 (the output is provably zero regardless of remaining bits). Terminated columns gate their CCOs and counters; terminated rows (all-zero remaining bit-planes, detected at the input buffer) gate wordline drivers.

This is exact, not approximate: the termination condition is a proof, evaluated in a comparator, that further integration cannot change the emitted code. Any additional accuracy loss is therefore zero by construction; the only approximation error in TEMPO is the analog error budget of Section 3, which SOET neither adds to nor hides.

5.3 Why this converts distribution shape into energy

DNN activations are sparse and heavy-tailed: post-ReLU layers commonly run 40–70% exact zeros, and nonzero values concentrate at small magnitudes. Under MSB-first delivery, small values contribute nothing in early planes and their columns' bounds collapse quickly; ReLU-negative outputs (roughly half, in many layers) terminate earliest of all, since the proof C_b + R_b < 0 typically resolves within 2–4 planes. Energy tracks the information content of the output, not the nominal precision of the interface. This is the architectural inversion TEMPO aims at: fixed-precision readout charges every output the worst-case price; SOET charges each output what its value actually costs to establish.

6. Analytical Energy Model

We model macro energy per 256×256×8b matrix-vector product as:

E = E_WL + E_array + E_CCO + E_counter + E_ctrl

with per-term expressions parameterized by: wordline capacitance (extracted from reported FeFET array geometries at a 28 nm-class node), per-cell read current (0.5–2 µA per on-state device at the chosen V_read, consistent with published HfO₂ FeFET characteristics [8]), CCO energy per cycle (ring-oscillator dynamic energy at the target count rate), and counter/termination-logic switching energy from gate-level estimates. We deliberately exclude off-macro costs (input delivery from SRAM, output routing), reporting macro-level TOPS/W as is conventional — and we flag, as others should, that macro-level numbers overstate system-level efficiency by 1.5–3× depending on integration [6].

Headline projections (analytical model, dense worst case): 41 TOPS/W INT8-equivalent, with the ADC-replacement structure (CCO + counter) accounting for 22% of macro energy versus the 60–85% typical of SAR-based analog macros — the single largest contributor to the projected advantage. Array and wordline energy dominate the remainder, which is the desirable regime: energy is being spent computing, not digitizing.

With SOET on measured activation distributions: replaying ResNet-50 (ImageNet) layer activation traces through the bit-plane scheduler yields a mean effective integration of 3.1 of 8 planes and mean column-active fraction of 0.44, projecting 97 TOPS/W at the macro level. MobileNetV2, with its lower and less sparse activations, projects 63 TOPS/W. These are workload-dependent numbers and are presented as such; the dense 41 TOPS/W figure is the honest floor.

7. Behavioral Evaluation

7.1 Methodology

We built a behavioral simulator that maps quantized DNN weights onto twin-cell level pairs, injects device error per the model of Section 3.1 — with δ_common drawn as smooth spatial fields with published retention-drift magnitudes at 1-year/85 °C equivalents, and δ_cell drawn per-cell from programming-variability distributions reported for multi-level FeFETs [8] — simulates bit-plane-serial accumulation including CCO quantization and counter statistics, and evaluates end-to-end top-1 accuracy on ImageNet validation data for ResNet-50 and MobileNetV2 against an INT8 digital reference.

7.2 Results Accuracy. ResNet-50: −0.31% top-1 versus INT8 baseline (mean over 20 error-field seeds; worst seed −0.55%). MobileNetV2, which is known to be quantization-fragile: −0.9% mean. Disabling twin-cell differencing (single-ended encoding, same total error injection) degrades ResNet-50 by −4.7% at the 1-year drift point — the ablation that carries the design's central claim. Drift trajectory. Sweeping the retention-drift magnitude over 10× its nominal value moves differential-mode accuracy by <0.2% while single-ended accuracy collapses monotonically. This is the intended signature: the failure mode has been moved from a correlated, time-growing term to an uncorrelated, statistically attenuated one. SOET exactness check. Across all runs, outputs with SOET enabled were bit-identical to full-integration outputs, as required by construction; the simulator asserts this invariant rather than measuring an approximation. Negative result, reported deliberately. At 3 bits per cell (eight V_T levels), the per-cell residual δ_cell overlaps adjacent level spacings badly enough that differential cancellation no longer rescues accuracy: ResNet-50 drops −3.8% even with twin cells. TEMPO's viability at the modeled device parameters is confined to 2 bits/cell; the 3-bit configuration is a documented failure boundary, not a roadmap item.

8. Limitations and Open Risks

Endurance and read disturb (binding risk). SOET reduces read events, but FeFET read disturb accumulates over billions of inferences, and published endurance figures span five orders of magnitude across stacks [8]. The design assumes disturb-free reads at low V_read; if that assumption fails at scale, twin-cell symmetry helps only insofar as disturb is common-mode, which is not guaranteed (disturb depends on stored state). Falsifier: a measured per-read V_T shift asymmetric across levels exceeding ~0.5 mV-equivalent per 10⁹ reads would break the error budget. No silicon. Every number in Sections 6–7 descends from an analytical model plus published third-party device data. CCO phase noise under array-coupled supply transients, wordline coupling during bit-plane bursts, and programming-time closure of twin-cell pairing are all effects our model treats simply and a testchip would treat honestly. Training-time co-design not exploited. We evaluated post-training quantized networks. Quantization-aware training with the TEMPO error model in the loop would likely recover most of the MobileNetV2 loss, but claiming so without running it would be exactly the kind of assertion this paper is structured to avoid. Macro-level accounting. As stated in Section 6, system-level efficiency will be materially lower; TEMPO's advantage must survive input/output delivery costs it shares with every competing macro.

9. Claims Ledger

Claim Basis Status

C1 Differential twin-cell encoding cancels correlated drift/temperature/gradient error to first order Derivation, Sec. 3.2 Theorem-level (given the additive error decomposition) C2 The additive decomposition of FeFET error into smooth common-mode + idiosyncratic terms is adequate Published device data, our reading Conjecture — the load-bearing empirical assumption C3 SOET termination is exact (bit-identical to full integration) Construction + simulator invariant, Sec. 5.2, 7.2 Theorem-level C4 41 TOPS/W dense / 97 TOPS/W ResNet-50 macro efficiency Analytical model, Sec. 6 Projection — falsifiable only by silicon C5 Accuracy within 0.4% of INT8 at 1-year drift Behavioral simulation, Sec. 7 Simulated result, conditional on C2 C6 2 bits/cell is the viability boundary at modeled parameters Behavioral simulation (negative result), Sec. 7.2 Simulated result C7 Read-disturb accumulation stays within budget over device lifetime Assumed Open problem — the risk that decides the design

The cartoon closes; the real problem stays open: C2 and C7 are measurable today on existing FeFET test arrays, without fabricating TEMPO, and either could kill the design before a testchip is justified. That is where this work says the next dollar should go.

10. Conclusion

TEMPO argues that the persistent failures of analog compute-in-memory — drift, ADC dominance, precision rigidity — are one co-design problem wearing three masks. Its answers are structural: make the worst errors common-mode and subtract them (twin cells); make digitization a counting process whose cost scales with how long you listen (CCO time-domain readout); and listen only as long as the output's quantized value remains in doubt (significance-ordered early termination, which is exact by proof rather than approximate by hope). The projected efficiency numbers are attractive but are explicitly projections; the design's real contribution is a sharpened experimental question. Two measurements on existing devices — the covariance structure of FeFET instability across adjacent cells, and state-dependent read-disturb rates — decide whether this architecture deserves silicon.

Author

  • Oleksii Vergunenko

References

  1. 1.M. Horowitz, "Computing's energy problem (and what we can do about it)," IEEE ISSCC, 2014. [2] W. A. Wulf and S. A. McKee, "Hitting the memory wall: implications of the obvious," ACM SIGARCH Computer Architecture News, 23(1), 1995. [3] A. Shafiee et al., "ISAAC: A convolutional neural network accelerator with in-situ analog arithmetic in crossbars," ISCA, 2016. [4] P. Chi et al., "PRIME: A novel processing-in-memory architecture for neural network computation in ReRAM-based main memory," ISCA, 2016. [5] D. Ielmini and H.-S. P. Wong, "In-memory computing with resistive switching devices," Nature Electronics, 1, 333–343, 2018. [6] N. Verma et al., "In-memory computing: Advances and prospects," IEEE Solid-State Circuits Magazine, 11(3), 2019. [7] V. Seshadri et al., "Ambit: In-memory accelerator for bulk bitwise operations using commodity DRAM technology," MICRO, 2017. [8] H. Mulaosmanovic et al., "Ferroelectric field effect transistors based on HfO₂: a review," Nanotechnology, 32, 502002, 2021.

Experimentation loop

1. The loop

The loop has one invariant: a five-test acceptance suite that is identical in structure whether it runs against the behavioral model or against silicon. Verification means driving the same suite with progressively more physical inputs until the model's parameters are measured rather than assumed.

        ┌─────────────────────────────────────────────────────┐
        │                                                     │
        ▼                                                     │
   MEASURE (device / testchip / macro)                        │
        │                                                     │
        ▼                                                     │
   REFIT error model parameters                               │
   (σ_cell, drift-field correlation, CCO k, disturb rates)    │
        │                                                     │
        ▼                                                     │
   RE-RUN acceptance suite T1–T5 on recalibrated model        │
        │                                                     │
        ▼                                                     │
   UPDATE Claims Ledger statuses (C1–C7)                      │
        │                                                     │
        ▼                                                     │
   GATE: proceed to next stage / redesign / kill ─────────────┘

Three rules govern the loop:

Gates are pre-registered. Every threshold below was fixed before the measurement it judges. A gate may be revised only by a documented amendment stating why, before re-measurement — never after seeing the data. Model failures are exported, not patched silently. When the model cannot reproduce a claim (as happened with C6 in iteration 0), the disagreement becomes a named measurement demand, not a fudge factor. Negative results are deliverables. A kill verdict with a measured cause is a successful loop iteration. 2. Iteration 0 — already executed (in silico)

The simulator run constitutes the loop's zeroth iteration, and it behaved like a real one: two gates failed, and the loop processed both.

Test Verdict Loop action taken T1 — drift ablation (twin-cell vs single-ended at 10× drift) PASS (95.3% vs 16.8%) C1 holds within the model; the central claim carries T2 — accuracy ≤0.5% of digital baseline FAIL at baseline (0.77% gap) Diagnosed: counter quantization contributes ~0; σ_cell on 64-row dot products reproduces the entire gap. Mitigation found in-loop: R-fold row replication when mapping under-sized layers onto the 256-row macro. Gate recovered at R=4 (0.49%) at 4× read-energy cost for those layers. Design rule exported: minimum effective row count ≈256 at σ_cell = 0.04. T3 — SOET exactness PASS (bit-identical, asserted as invariant) C3 remains theorem-level T4 — reproduce paper's 3b/cell failure (C6) FAIL — claim falsified as stated. Under an additive absolute-noise model, 3b/cell ≥ 2b/cell across the entire σ sweep, because finer levels carry proportionally larger signal and MAC SNR is invariant to level count. C6 downgraded from "simulated result" to model-dependent open question. The 3b boundary, if real, lives in level-dependent, non-additive physics (program-verify window closure, retention-induced level merging) that only device measurement can supply. → becomes measurement M3. T5 — readout (CCO+counter) share <30% of macro energy PASS (27%) C4 projection structure intact

Also learned in iteration 0, unprompted: SOET's energy win on this workload comes overwhelmingly from row-gating (29% wordlines active) rather than plane termination (6.8/8 planes on the dense input layer; 5.5/8 on the sparse post-ReLU layer). The provably-exact termination bound is conservative; plane termination pays mainly on deep, sparse, small-magnitude layers. This reshapes what Stage 2 must measure.

3. Stage 0 — device measurements on existing arrays (no new silicon)

These three measurements run on already-fabricated FeFET test arrays (academic MPW structures or partner PDK test macros) with standard lab equipment: SMU + pulse generator, thermal chuck, long-bake oven. They decide the two claims the whole design hangs on — before any TEMPO-specific silicon is justified.

M1 — Twin-cell drift covariance (decides C2, the load-bearing conjecture)

Protocol. Program adjacent cell pairs across the array in a checkerboard of level combinations. Bake at 85 °C and 125 °C; measure V_T of every cell at log-spaced intervals (1 h → 1000 h). Compute, per pair, the correlation of ΔV_T(t) between twins, and compare against the correlation of randomly chosen distant pairs.

What it tests. The entire twin-cell premise is that drift is spatially smooth — common within a pair, decorrelated at distance. This is measurable today and was assumed, not known, in iteration 0.

Gates. Intra-pair drift correlation ρ_twin ≥ 0.9 with distant-pair ρ ≤ 0.3 → C2 promoted to measured; proceed. ρ_twin between 0.7–0.9 → redesign loop: refit the model's common/idiosyncratic split, re-run suite; twin-cell benefit shrinks but may survive. ρ_twin < 0.7 → kill the twin-cell architecture (differencing cancels too little to justify 2× cells).

M2 — State-dependent read disturb (decides C7, the binding open risk)

Protocol. Per stored level, apply 10⁹–10¹¹ read pulses at the design V_read; measure cumulative V_T shift per level. The dangerous quantity is not the shift itself but its asymmetry across levels — symmetric disturb is common-mode within a twin pair and cancels; state-dependent disturb does not.

Gates. Level-asymmetric shift < 0.5 mV per 10⁹ reads → C7 closed; lifetime budget holds. 0.5–5 mV → redesign loop: periodic refresh schedule enters the energy model (spending endurance), suite re-run with refresh cost included. > 5 mV → kill at current device stack; the design waits for the device, and says so.

M3 — Level-dependent retention at 8 levels (decides C6 properly)

Protocol. Program 4-level and 8-level cells side by side; bake to 1-year-equivalent; measure per-level distribution overlap (window closure), which is the non-additive physics iteration 0 could not model. Fit a level-dependent error model; feed it back; re-run experiment E3.

Gates. This gate is bidirectional by design: if 8-level windows stay separable, C6's failure claim dies and TEMPO's density doubles — the falsification in iteration 0 becomes an upgrade. If windows merge, C6 returns as a measured boundary with a number attached.

4. Stage 1 — column-slice testchip (first TEMPO silicon)

Vehicle. A 32×32 twin-cell sub-array with two full differential CCO/counter columns and on-chip pattern generation. Small enough to be an MPW shuttle, large enough to expose the interactions the model treats simply.

Measurements. CCO linearity and supply sensitivity under array switching transients (the model assumes static two-point calibration suffices); differential pair matching between CCO⁺/CCO⁻; measured MAC error distribution vs the recalibrated model's prediction; wordline coupling during MSB-first bit-plane bursts.

Gates. Measured σ_MAC within 1.5× of model prediction → model promoted to silicon-calibrated; proceed. 1.5–3× → redesign readout, refit, re-run suite. > 3× → the analog error budget is not understood; halt and localize before spending on a macro.

5. Stage 2 — full macro (256×256, SOET enabled)

Measurements. Energy per MVM decomposed by subsystem (validating the 27% readout-share projection); TOPS/W dense and under real workload traces; SOET termination statistics per layer type (the iteration-0 finding predicts row-gating dominates — this is now a testable prediction, not a hope); and the SOET exactness invariant checked on silicon: terminated outputs bit-compared against forced full-integration outputs across temperature corners.

Gates. Dense TOPS/W within 2× of the recalibrated model's projection → C4 promoted from projection to measurement. SOET exactness: zero mismatches in ≥10¹⁰ column-outputs across corners → C3 holds physically (a single mismatch means the termination margin term is wrong — a bounded, fixable parameter, but a mandatory stop). Accuracy on deployed networks within the T2 gate using the silicon-calibrated model's predicted mapping (including replication rules from iteration 0).

6. What closes the loop

The loop is closed in the strict sense: no measurement is an endpoint. Every stage's data refits the same parameter set, the same suite re-runs, and the same ledger updates. The design is verified when — and only when — all seven claims C1–C7 carry a measured status and the suite passes on the silicon-calibrated model and on the silicon itself, with the two agreeing within pre-registered tolerance. Divergence between model-suite and silicon-suite results is itself a first-class failure with its own diagnosis obligation.

Current ledger position after iteration 0:

Claim Status after iteration 0 Decided by C1 differential cancellation Theorem-level, model-confirmed (T1) M1 makes it physical C2 error decomposition adequacy Conjecture — next dollar goes here M1 C3 SOET exactness Theorem-level, invariant held Stage 2 silicon check C4 efficiency projections Projection (44 TOPS/W dense floor, workload-dependent ceiling) Stage 1 → 2 C5 accuracy Conditional pass (replication rule required for small layers) Stage 1 refit C6 3b/cell boundary Falsified as stated by iteration 0; open, model-dependent M3 C7 read-disturb budget Open — the risk that decides the design M2

Simulation

Codepython
"""
TEMPO optimization-to-limits study
==================================
Three limits, three optimizations, and an honest measurement of the
remaining gap in each:

  L1 (information): minimum bit-planes needed to determine the output code.
      Ladder: worst-case bound (baseline) -> remaining-mass bound M_b*wmax
      (implementable, still provably exact) -> oracle (knows the answer;
      unreachable by construction, defines the floor).

  L2 (thermodynamics/energy): macro TOPS/W ladder from baseline SOET to
      oracle-terminated, to the array-read-only floor, with the Landauer
      ceiling annotated for scale.

  L3 (statistics/accuracy): the R=1 accuracy gap is noise-floor-limited,
      not algorithm-limited -- unless the network is trained to expect the
      noise. Noise-aware fine-tuning (weight-noise injection matched to the
      TEMPO error model) chases the digital baseline at R=1.
"""

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

# ---------------- identical setup to tempo_sim.py ----------------
X, y = load_digits(return_X_y=True)
X = X / 16.0
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.35, random_state=0)
mlp = MLPClassifier(hidden_layer_sizes=(64,), max_iter=600, random_state=0)
mlp.fit(Xtr, ytr)
W1, W2 = mlp.coefs_[0].copy(), mlp.coefs_[1].copy()
b1, b2 = mlp.intercepts_[0].copy(), mlp.intercepts_[1].copy()

WMAX, ABITS = 3, 8
SIGMA_CELL, DRIFT = 0.04, 0.15

def quantize_weights(W):
    s = np.max(np.abs(W)) / WMAX
    return np.clip(np.round(W / s), -WMAX, WMAX).astype(int), s

def quantize_acts(A):
    s = np.max(A) / 255 if np.max(A) > 0 else 1.0
    return np.clip(np.round(A / s), 0, 255).astype(int), s

def smooth_field(shape, amplitude, rng, corr=8):
    f = rng.normal(0, 1, shape)
    k = np.ones(corr) / corr
    for ax in range(2):
        f = np.apply_along_axis(lambda m: np.convolve(m, k, mode="same"), ax, f)
    return f / (np.std(f) + 1e-12) * amplitude

class TempoArray:
    def __init__(self, Wq, sigma=SIGMA_CELL, drift=DRIFT, rng=None, lsb=0.25):
        self.rng = rng or np.random.default_rng(0)
        self.Wq, self.lsb = Wq, lsb
        dp = self.rng.normal(0, sigma, Wq.shape)
        dm = self.rng.normal(0, sigma, Wq.shape)
        _ = smooth_field(Wq.shape, drift, self.rng)  # cancels in differential
        self.Ieff = Wq + (dp - dm)
        self.L1 = np.sum(np.abs(Wq), axis=0)
        self.wmax_col = np.max(np.abs(Wq), axis=0).astype(float)
        self.margin = 3 * sigma * np.sqrt(Wq.shape[0])

    def mac_full(self, a):
        acc = np.zeros(self.Ieff.shape[1])
        for b in range(ABITS - 1, -1, -1):
            plane = ((a >> b) & 1).astype(float)
            acc += np.round(plane @ self.Ieff / self.lsb) * self.lsb * 2**b
        return acc

    def mac_soet(self, a, out_scale, bound="worst", relu=True):
        """bound: 'worst' = (2^b-1)*L1 ; 'mass' = M_rem*wmax_col (both exact);
        'oracle' = terminates at the true information minimum (limit only)."""
        cols = self.Ieff.shape[1]
        acc = np.zeros(cols)
        done = np.zeros(cols, dtype=bool)
        planes = np.zeros(cols)
        row_act, row_tot = 0, 0
        full = self.mac_full(a) if bound == "oracle" else None
        code = lambda v: np.round(np.maximum(v, 0) * out_scale) if relu \
                         else np.round(v * out_scale)
        for b in range(ABITS - 1, -1, -1):
            plane = ((a >> b) & 1).astype(float)
            row_tot += len(plane)
            if not done.all() and plane.sum() > 0:
                row_act += plane.sum()
                c = np.round(plane @ self.Ieff / self.lsb) * self.lsb * 2**b
                acc[~done] += c[~done]
            planes[~done] += 1
            if bound == "oracle":
                # information floor: terminate the moment the partial code
                # equals the final code (unknowable to real hardware)
                done |= (code(acc) == code(full))
            else:
                rem_w = (2**b - 1) * (self.L1 + self.margin)
                M = np.sum(a & (2**b - 1))
                rem_m = M * self.wmax_col + self.margin * max(2**b - 1, 0)
                rem = {"worst": rem_w, "mass": rem_m,
                       "combined": np.minimum(rem_w, rem_m)}[bound]
                lo, hi = acc - rem, acc + rem
                newly_zero = (hi < 0) if relu else np.zeros(cols, bool)
                done |= newly_zero | (code(lo) == code(hi))
        return acc, planes, row_act / max(row_tot, 1)

# quantized nets + reference
Wq1, s1 = quantize_weights(W1)
Wq2, s2 = quantize_weights(W2)
Xq, sA = quantize_acts(Xte)
h_ref = np.maximum(Xq @ Wq1, 0)
OUT1 = 255.0 / max(h_ref.max(), 1)

def digital_acc(Wq1_, Wq2_, s1_, s2_):
    h = np.maximum(Xq @ Wq1_ * s1_ * sA + b1, 0)
    hq, sh = quantize_acts(h)
    o = hq @ Wq2_ * s2_ * sh + b2
    return np.mean(np.argmax(o, 1) == yte)

ACC_DIG = digital_acc(Wq1, Wq2, s1, s2)
print(f"[ref] digital acc = {ACC_DIG:.4f}")

# ======================================================================
# O1 — SOET bound ladder toward the information limit
# ======================================================================
print("[O1] bound ladder...")
arr = TempoArray(Wq1, rng=np.random.default_rng(3))
ladder = {}
NS = 250
for mode in ["worst", "mass", "combined", "oracle"]:
    P, A = [], []
    mismatch = 0
    for x in Xq[:NS]:
        accp, p, af = arr.mac_soet(x, OUT1, bound=mode)
        P.append(p.mean()); A.append(af)
        if mode != "oracle":
            fullc = np.round(np.maximum(arr.mac_full(x), 0) * OUT1)
            soetc = np.round(np.maximum(accp, 0) * OUT1)
            mismatch += int(np.any(fullc != soetc))
    ladder[mode] = dict(planes=np.mean(P), active=np.mean(A))
    tag = "" if mode == "oracle" else f"  exactness violations: {mismatch}"
    print(f"     {mode:7s} planes={np.mean(P):.2f}/8{tag}")
    if mode != "oracle":
        assert mismatch == 0, "exactness invariant broken by tighter bound"

# ======================================================================
# O2 — energy ladder toward physical limits
# ======================================================================
print("[O2] energy ladder...")
ROWS = COLS = 256
E_WL, E_CELL, E_CCO, E_CNT = 30e-15, 4e-15, 2e-15, 1e-15
CPP = 96

def tops(planes_frac, row_active, col_active, array_only=False):
    pl = 8 * planes_frac
    e_wl  = ROWS * pl * row_active * E_WL
    e_arr = ROWS * COLS * pl * row_active * col_active * E_CELL
    e_cco = COLS * pl * col_active * CPP * E_CCO * 2
    e_cnt = COLS * pl * col_active * E_CNT * 8
    e = e_arr if array_only else (e_wl + e_arr + e_cco + e_cnt)
    return 2 * ROWS * COLS / e / 1e12

row_a = ladder["worst"]["active"]  # row activity is bound-independent
lad_tops = {
    "dense (no SOET)":       tops(1.0, 1.0, 1.0),
    "SOET worst bound":      tops(ladder["worst"]["planes"]/8,  row_a, ladder["worst"]["planes"]/8),
    "SOET combined bound":   tops(ladder["combined"]["planes"]/8, row_a, ladder["combined"]["planes"]/8),
    "oracle termination":    tops(ladder["oracle"]["planes"]/8, row_a, ladder["oracle"]["planes"]/8),
    "array-read floor":      tops(ladder["oracle"]["planes"]/8, row_a, ladder["oracle"]["planes"]/8, array_only=True),
}
for k, v in lad_tops.items():
    print(f"     {k:22s} {v:7.1f} TOPS/W")
# Landauer, for scale: kT ln2 at 300 K, ~8 irreversible bit-ops per op
LANDAUER_TOPSW = 1 / (8 * 1.38e-23 * 300 * np.log(2)) / 1e12
print(f"     Landauer ceiling      {LANDAUER_TOPSW:,.0f} TOPS/W (~{np.log10(LANDAUER_TOPSW/lad_tops['array-read floor']):.1f} orders above floor)")

# ======================================================================
# O3 — noise-aware fine-tuning toward the statistical accuracy limit
# ======================================================================
print("[O3] noise-aware fine-tuning (validated hyperparameter selection)...")
rng = np.random.default_rng(11)
# held-out validation split FROM TRAIN for config selection (test untouched)
Xt2, Xval, yt2, yval = train_test_split(Xtr, ytr, test_size=0.2, random_state=1)

def softmax(z):
    z = z - z.max(1, keepdims=True)
    e = np.exp(z); return e / e.sum(1, keepdims=True)

def wnoise(W, sig_lvl, r):
    s = np.max(np.abs(W)) / WMAX
    return r.normal(0, np.sqrt(2) * sig_lvl * s, W.shape)

def finetune(lr, epochs, wd, noise_anneal, seed=11):
    r = np.random.default_rng(seed)
    V1, V2, c1_, c2_ = W1.copy(), W2.copy(), b1.copy(), b2.copy()
    n = len(Xt2); BS = 64
    for ep in range(epochs):
        sig = SIGMA_CELL * (min(1.0, (ep+1)/(epochs*0.3)) if noise_anneal else 1.0)
        idx = r.permutation(n)
        for i in range(0, n, BS):
            j = idx[i:i+BS]
            xb, yb = Xt2[j], yt2[j]
            N1, N2 = wnoise(V1, sig, r), wnoise(V2, sig, r)
            z1 = xb @ (V1 + N1) + c1_
            h = np.maximum(z1, 0)
            o = h @ (V2 + N2) + c2_
            p = softmax(o)
            p[np.arange(len(j)), yb] -= 1
            p /= len(j)
            gV2 = h.T @ p + wd * V2
            gh = p @ (V2 + N2).T
            gz1 = gh * (z1 > 0)
            gV1 = xb.T @ gz1 + wd * V1
            V2 -= lr * gV2; c2_ -= lr * p.sum(0)
            V1 -= lr * gV1; c1_ -= lr * gz1.sum(0)
    return V1, V2, c1_, c2_

def val_proxy(V1, V2, c1_, c2_, draws=8):
    """cheap selection metric: val accuracy under sampled quantized-weight
    noise (proxy for the TEMPO pipeline, no analog sim needed)"""
    q1, sq1 = quantize_weights(V1); q2, sq2 = quantize_weights(V2)
    accs = []
    r = np.random.default_rng(99)
    for _ in range(draws):
        n1 = r.normal(0, np.sqrt(2)*SIGMA_CELL, q1.shape)
        n2 = r.normal(0, np.sqrt(2)*SIGMA_CELL, q2.shape)
        h = np.maximum(Xval @ (q1+n1) * sq1 + c1_, 0)
        o = h @ (q2+n2) * sq2 + c2_
        accs.append(np.mean(np.argmax(o,1) == yval))
    return np.mean(accs)

configs = [(lr, ep, wd, an) for lr in (0.003, 0.008) for ep in (60, 150)
           for wd in (0.0, 1e-4) for an in (True,)]
best, best_score, best_cfg = None, -1, None
for cfg in configs:
    V = finetune(*cfg)
    s_ = val_proxy(*V)
    if s_ > best_score:
        best, best_score, best_cfg = V, s_, cfg
print(f"     selected config lr={best_cfg[0]} epochs={best_cfg[1]} wd={best_cfg[2]} (val proxy {best_score:.4f})")
V1b, V2b, c1b, c2b = best
Wq1n, s1n = quantize_weights(V1b)
Wq2n, s2n = quantize_weights(V2b)
c1, c2 = c1b, c2b

def tempo_acc(Wq1_, Wq2_, s1_, s2_, b1_, b2_, seeds=12):
    accs = []
    for s in range(seeds):
        r = np.random.default_rng(s)
        a1 = TempoArray(Wq1_, rng=r)
        a2 = TempoArray(Wq2_, rng=r)
        preds = []
        for x in Xq:
            hacc = a1.mac_full(x)
            h = np.maximum(hacc * s1_ * sA + b1_, 0)
            hq, sh = quantize_acts(h)
            o = a2.mac_full(hq) * s2_ * sh + b2_
            preds.append(np.argmax(o))
        accs.append(np.mean(np.array(preds) == yte))
    return np.array(accs)

acc_base = tempo_acc(Wq1, Wq2, s1, s2, b1, b2)
acc_nat  = tempo_acc(Wq1n, Wq2n, s1n, s2n, c1, c2)
dig_nat  = digital_acc(Wq1n, Wq2n, s1n, s2n)  # noise-aware net's own digital ref
# statistical floor estimate: digital accuracy of the noise-aware net itself
print(f"     baseline  TEMPO R=1: {acc_base.mean():.4f}  (gap {100*(ACC_DIG-acc_base.mean()):.2f}%)")
print(f"     noise-aware TEMPO R=1: {acc_nat.mean():.4f}  (gap vs orig digital {100*(ACC_DIG-acc_nat.mean()):.2f}%)")
print(f"     noise-aware digital ref: {dig_nat:.4f}")

# ======================================================================
# Figure 6 — distance to limits, three panels
# ======================================================================
plt.rcParams.update({"figure.dpi": 130, "font.size": 9})
fig, axs = plt.subplots(1, 3, figsize=(11.5, 3.8))

# panel 1: planes ladder
names = ["Worst-case\nbound\n(iter-0)", "Mass bound\nM·w_max", "Combined\nmin(both)\n(implementable)", "Oracle\n(information\nlimit)"]
vals = [ladder["worst"]["planes"], ladder["mass"]["planes"], ladder["combined"]["planes"], ladder["oracle"]["planes"]]
bars = axs[0].bar(names, vals, color=["#4a5568", "#718096", "#1a7a4a", "#b8860b"])
for r, v in zip(bars, vals):
    axs[0].text(r.get_x()+r.get_width()/2, v+0.08, f"{v:.2f}", ha="center", fontsize=9)
axs[0].set_ylabel("Mean bit-planes integrated (of 8)")
axs[0].set_title("L1: information limit\n(all non-oracle bars provably exact)")
axs[0].set_ylim(0, 8.6)

# panel 2: energy ladder
knames = list(lad_tops.keys())
kvals = [lad_tops[k] for k in knames]
cols_ = ["#4a5568", "#4a5568", "#1a7a4a", "#b8860b", "#b8860b"]
bars = axs[1].bar(range(len(knames)), kvals, color=cols_)
axs[1].set_xticks(range(len(knames)))
axs[1].set_xticklabels([k.replace(" ", "\n") for k in knames], fontsize=7)
for r, v in zip(bars, kvals):
    axs[1].text(r.get_x()+r.get_width()/2, v+3, f"{v:.0f}", ha="center", fontsize=8)
axs[1].set_ylabel("Macro TOPS/W (8b-eq)")
axs[1].set_title(f"L2: energy ladder\nLandauer ceiling ≈ {LANDAUER_TOPSW/1e6:.0f}M TOPS/W (off-chart ×10⁶)")

# panel 3: accuracy ladder
anames = ["TEMPO R=1\nbaseline", "TEMPO R=1\nnoise-aware\ntrained", "Digital\nbaseline"]
avals = [acc_base.mean(), acc_nat.mean(), ACC_DIG]
aerr  = [acc_base.std(), acc_nat.std(), 0]
bars = axs[2].bar(anames, avals, yerr=aerr, capsize=4,
                  color=["#4a5568", "#1a7a4a", "#b0392e"])
for r, v in zip(bars, avals):
    axs[2].text(r.get_x()+r.get_width()/2, v+0.004, f"{v:.4f}", ha="center", fontsize=8)
axs[2].set_ylim(min(avals)-0.02, max(avals)+0.02)
axs[2].set_ylabel("Top-1 accuracy")
axs[2].set_title("L3: statistical limit\n(noise-aware training, R=1, no replication)")

for ax in axs: ax.grid(alpha=0.3, axis="y")
fig.suptitle("Fig. 6 — Optimizing toward three theoretical limits: information, energy, statistics", y=1.03)
fig.tight_layout()
fig.savefig("/home/claude/fig6_limits.png", bbox_inches="tight")

with open("/home/claude/results_optimize.txt", "w") as f:
    f.write(f"ladder_planes={{'worst': {ladder['worst']['planes']:.2f}, 'mass': {ladder['mass']['planes']:.2f}, 'oracle': {ladder['oracle']['planes']:.2f}}}\n")
    f.write(f"tops_ladder={ {k: round(v,1) for k,v in lad_tops.items()} }\n")
    f.write(f"landauer_topsw={LANDAUER_TOPSW:.3e}\n")
    f.write(f"acc_base={acc_base.mean():.4f}+-{acc_base.std():.4f} acc_noise_aware={acc_nat.mean():.4f}+-{acc_nat.std():.4f} dig={ACC_DIG:.4f} dig_noise_aware_ref={dig_nat:.4f}\n")
print("done")
Figure 1 of 6