Two identical HFETs, each biased by its own voltage sources. z1 has vds = -1 V, z2 has vds = +1 V. Order B is order A with the two Z lines swapped.
HFETA instance-order test, order A
vd1 d1 0 dc -1
vd2 d2 0 dc 1
vg g 0 dc 0
z1 d1 g 0 hf l=1u w=10u
z2 d2 g 0 hf l=1u w=10u
.model hf nhfet level=5 vt0=-0.3 rd=60 rs=60
.control
op
print i(vd1) i(vd2)
.endc
.end
Expected: both orders give the same currents. Each device run alone gives i(vd1) = 6.244326e-04 and i(vd2) = -4.38545e-04.
Actual (ngspice -b):
| i(vd1) | i(vd2) | |
|---|---|---|
| order A (z1 line first) | 6.244326e-04 | -4.38545e-04 |
| order B (z2 line first) | 6.244326e-04 | 2.463106e-04 |
In order B, z2 carries current with the wrong sign.
The shipped test tests/hfet/inverter.cir shows the same problem. Change the .tran to .op and move the z2 line above z1 in the subcircuit. That gives v(3) = 1.955693, v(4) = 0.2063419, i(vdd) = -5.75454e-04. The deck as shipped gives v(3) = -0.27545, v(4) = -0.275539, i(vdd) = 1.32e-09. The shipped result is the wrong one: each depletion load sits at vgs = 0 with about 2.27 V across it, yet i(vdd) is about 1 nA.
src/spicelib/devices/hfet1/hfetload.c, HFETAload():
int inverse=FALSE; is set once per call.if(vds < 0) { vds = -vds; inverse = TRUE; }if(inverse) { cdrain = -cdrain; vds = -vds; swap capgs/capgd }Nothing resets inverse inside the instance loop. Once one instance with vds < 0 has been loaded, every instance loaded after it in the same HFETAload call is also treated as inverted: its drain current is negated and Cgs/Cgd are swapped. The result therefore depends on instance order.
Reset the flag per instance (patch against 3ef069f attached, hfetload-inverse.patch):
--- a/src/spicelib/devices/hfet1/hfetload.c
+++ b/src/spicelib/devices/hfet1/hfetload.c
@@ -80,7 +80,7 @@ int HFETAload(GENmodel *inModel, CKTcircuit *ckt)
double gmg=0;
double gmd=0;
- int inverse=FALSE;
+ int inverse;
int icheck;
int error;
@@ -93,6 +93,7 @@ int HFETAload(GENmodel *inModel, CKTcircuit *ckt)
vcrit = here->HFETAvcrit;
vt = CONSTKoverQ * here->HFETAtemp;
icheck = 0;
+ inverse = FALSE;
if( ckt->CKTmode & MODEINITSMSIG) {
vgs = *(ckt->CKTstate0 + here->HFETAvgs);
vgd = *(ckt->CKTstate0 + here->HFETAvgd);
With the patch, both orders give i(vd1) = 6.244326e-04 and i(vd2) = -4.38545e-04. Both orders of the inverter .op give v(3) = 1.955693, v(4) = 0.2063419, i(vdd) = -5.75454e-04.
Side effect: the reference file tests/hfet/inverter.out was generated with the bug (its t = 0 value is v(3) = -0.2754464). It needs to be regenerated. Unpatched master matches it within 4e-6 V. Patched master differs by up to 2.23 V, starting from v(3) = 1.955693 at t = 0.
The bug was found and analysed with AI assistance. It was confirmed by building ngspice master and running it unpatched and patched on the decks above.
Diff:
Fix on git branch pre-master-48