Menu ▾ ▴

#877 HFET1 (level 5): drain current of an instance depends on netlist order; `inverse` flag in HFETAload is never reset

v1.0 (example)
open-fixed
None
5
2026-10-12
2026-10-12
No

Version / platform

  • ngspice git master 3ef069fb1f04177a153f342a32d941fc20ff047e ("Fixes bug 876", 2026-10-09), reports itself as ngspice-47+
  • macOS 27.0.1 (arm64), Apple clang 21.0.0, bison 3.8.2
  • configure --with-x=no --disable-debug --enable-cider=no --enable-xspice=no --with-readline=no --disable-openmp

Input

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 vs actual

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.

Cause

src/spicelib/devices/hfet1/hfetload.c, HFETAload():

  • line 83: int inverse=FALSE; is set once per call.
  • lines 297-300: if(vds < 0) { vds = -vds; inverse = TRUE; }
  • line 305: 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.

Fix

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.

Discussion

  • Holger Vogt

    Holger Vogt - 2026-10-12
    • Description has changed:

    Diff:

    
    
    • status: --> open-accepted
    • assigned_to: Holger Vogt
    • Group: --> v1.0 (example)
    • Priority: --> 5
     
  • Holger Vogt

    Holger Vogt - 2026-10-12
    • status: open-accepted --> open-fixed
     
  • Holger Vogt

    Holger Vogt - 2026-10-12

    Fix on git branch pre-master-48

     

Log in to post a comment.