# Native high-field ("tanh") resistor

This fork extends the built-in ngspice resistor device with a **high-field
voltage term**, so voltage-dependent poly resistors written by foundry PDKs as
behavioral expressions can be modeled as a **native device** instead of being
expanded into a behavioral `B`-source (ASRC).

It is the resistor counterpart of the native voltage/temperature-dependent
**capacitor** already in this fork, and is applied the same way: a one-time,
offline netlist rewrite with `tools/tsmc2ngspice`, after which the simulator
never has to parse or guess foundry expression syntax.

## Model

On top of the existing `tc1/tc2` (temperature) and `vc1/vc2/vsmooth`
(polynomial voltage) coefficients, three parameters `pvc1/pvc2/pvc3` add:

```
R(V,T) = R0 * (1 + tc1*dT + tc2*dT^2)
            * (1 + vc1*|V| + vc2*V^2)
            * (1 + pvc1*(tanh(pvc2*|E| + pvc3) - tanh(pvc3)))
```

where the high-field variable is `E = |V|/Leff`:

- **geometry / body segment** — `Leff = (L - 2*short)`, the electrical length
  already used for the conductance, so `E = |V|/L`.
- **value-defined / end segment** — no physical length, so `Leff = 1` and
  `E = |V|` (matches a foundry end-resistor law that uses `abs(v)` directly).

One model therefore serves both segment types. `|V|` is smoothed as
`sqrt(V^2 + vsmooth^2)` for a continuous Newton Jacobian, and the analytic
`dR/dV` is stamped via the companion model.

New model/instance parameters: `pvc1`, `pvc2`, `pvc3` (instance overrides
model, default 0). The feature is inactive unless at least one is given, so
existing netlists are unaffected.

## Why native instead of behavioral

A behavioral `B`-source is an interpreted parse tree: every device load walks
the expression *and* its symbolic derivatives and gathers `v()` from the
solution vector. The native device evaluates one `tanh` plus a closed-form
`dR/dV` — no tree walk. On designs where voltage-dependent poly resistors are a
large share of the elements, converting them to native form removes a
correspondingly large share of the per-iteration device-load cost, at
bit-equivalent accuracy.

## Performance

Measured on a representative large mixed-signal design (~85k devices: ~67k BSIM
transistors, ~9k resistors, ~8k capacitors).

Voltage-dependent poly resistors — written by the PDK as `v()`-dependent
expressions and therefore expanded by ngspice into behavioral `B`-sources
(ASRC) — dominated the cost out of all proportion to their count. A handful of
subckt definitions instantiated thousands of times produced **~9,100 such
resistors (~11% of all devices)**, yet they accounted for **~54% of
per-iteration device-load time — more than all ~67k BSIM transistors combined
(~44%)**. Each behavioral `B`-source is an interpreted parse tree: every load
walks the expression *and* its symbolic derivatives and gathers node voltages
from the solution vector; the native device is compiled arithmetic with an
analytic `dR/dV`.

Converting them to the native form:

| phase | native vs behavioral |
|---|---|
| full transient (wall time) | **~1.66x faster** |
| device-load phase | ~1.96x faster |
| matrix factor / solve | ~unchanged |
| timepoints / Newton iterations | ~unchanged (+-0.1%) |

Accuracy is preserved: DC sweeps are bit-exact vs the behavioral form, and
full-transient waveforms match to numerical-noise level. Timepoint and iteration
counts are unchanged, so the gain is purely lower per-load cost, not altered
convergence.

## Using it

Convert a behavioral model library once (see `tools/tsmc2ngspice`), which emits
`.model … R (… pvc1 pvc2 pvc3 …)` cards plus value-defined resistor instances;
then run the design unchanged. Or write the native form by hand:

```
.model rmod R tnom=25 vsmooth=1e-9 pvc1=... pvc3=... tc1=... tc2=...
r1 n1 n2 rmod 'R0' pvc2='<pvc2>/Leff'   ; body (field=|V|/Leff)
r2 n1 n2 rmod 'R0' pvc2='<pvc2>'        ; end  (field=|V|)
```

## Validation

`polyr_tanh_selftest.cir` is a self-contained regression test (generic, non-PDK
coefficients): it drives a native tanh resistor and the mathematically
equivalent behavioral `B`-source with the same law and checks they agree, for
both field conventions. Run it with:

```
ngspice -b polyr_tanh_selftest.cir
```

Expected: `vecmax` of the current difference on the order of 1e-19 A (currents
are ~mA) — i.e. bit-equal.

## Note

This documentation and the self-test use only generic coefficients. Foundry
model files and PDK coefficient values are proprietary and are **not** part of
this repository.
