diff --git a/.trinity/seals/analog_AnalogSarKickNet.json b/.trinity/seals/analog_AnalogSarKickNet.json new file mode 100644 index 0000000000..04ac7cbd27 --- /dev/null +++ b/.trinity/seals/analog_AnalogSarKickNet.json @@ -0,0 +1,53 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+42a4860c4", + "closure": [ + { + "spec": "specs/analog/linear.t27", + "spec_hash": "sha256:3ddef7be183aba583ed285334ee389b48a65b41335e98914514235db7c4e5d62" + }, + { + "spec": "specs/analog/mos_psp103_50c_data.t27", + "spec_hash": "sha256:9e68629797a29516836ca3e0b5e4881b0e751d847c58fd52b63e8f69746dbbe1" + }, + { + "spec": "specs/analog/mos_psp103_data.t27", + "spec_hash": "sha256:3936594d2ab220f06b2d636182689811b696e044fc3ce6850ea2604ac74f3ea3" + }, + { + "spec": "specs/analog/mos_table.t27", + "spec_hash": "sha256:16d28f10fa8663926dddb4bcced981cdc4cd8f38a6b748e4b7688f2e56bbab6c" + }, + { + "spec": "specs/analog/sar_cdac.t27", + "spec_hash": "sha256:a7c45039214a8b2e92333a88befc95af33ff482ce08af3be97e2c5b8be3eacd3" + }, + { + "spec": "specs/analog/sar_kick.t27", + "spec_hash": "sha256:8c7c3968475c3f62c78f7b0a7739c2f049680eb9911d23be7f4d0ce0a4922dff" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:8cb91e670ed91a22726cec0defd9e5edd7abc3899c0180bfe974425b7694adda", + "gen_hash_rust": "sha256:ddc364af3bf9d50c1aa3ddc1a56d4172316f9bac6a009b065c80fc84e1ee62e4", + "gen_hash_verilog": "sha256:9a0c3536cde4c2cbfea630b9e999c6396c4e93efaec1abc64af39ab41153609c", + "gen_hash_zig": "sha256:ab44ba61d3d916294e8a4b3c754d411d9e168192dfecb06ce6ad9dff36955cb6", + "module": "AnalogSarKickNet", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T18:11:39Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:54ed560c6e5fda7f9e497d03546dd36300d0b0e14da99dc3284bb76136bbf3b6", + "spec_path": "specs/analog/sar_kick_net.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 20, + "total": 20, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/analog/decks/sar_kick_aperture.cir b/specs/analog/decks/sar_kick_aperture.cir index 59b4352511..f0b6ccaa77 100644 --- a/specs/analog/decks/sar_kick_aperture.cir +++ b/specs/analog/decks/sar_kick_aperture.cir @@ -5,8 +5,10 @@ The golden comparator's aperture: how much a step of its input at time tau still * small step and the ratio of the response to a step at tau to the response to a step before the edge is the share of the decision that the input still decides at tau: 1 until the pair * begins to lose its gain and 0 once the latch has locked. The kick of the comparator's own plates moves VDP - VDN through that window, and sar_kick_net.t27 weighs it by this aperture. * The unperturbed run has dp = dn (the plates are alike); the steps at 98.3069 ns (before the edge), at 0.5 mV and at 2 mV, give the same response to 0.1 percent (LINEAR rows). -* Paths are NOT in this file: IHP_PDK, IHP_OSDI and SAR_GOLDEN_SUBCKTS as in sar_kick_edge.cir. Run: ngspice -b sar_kick_aperture.cir (ngspice-47, about two minutes) -* It prints one row per tau between BEGIN aperture and END aperture: tau - 99.3069 ns (the clkc 0.1 V crossing of the unperturbed run) in ns, and the response in volts. +* Paths are NOT in this file: IHP_PDK, IHP_OSDI and SAR_GOLDEN_SUBCKTS as in sar_kick_edge.cir. Run: ngspice -b sar_kick_aperture.cir (ngspice-47, 20 transients of 107 ns at 50 ps) +* It prints, between BEGIN aperture and END aperture, the rows APERTURE tau (tau is absolute: the step at 98.3069 ns, 1 ns before the clkc 0.1 V crossing of the unperturbed run at +* 99.3069 ns, then at 102.3069 ns + 0.125 ns j, 3.0 ns + 0.125 ns j after it, j = 0 .. 15, dv = 1 mV) and LINEAR dv (the step at 98.3069 ns at dv = 0, 0.5 mV, 2 mV), each with dd = v(x3.dp) +* and de = v(x3.dn) at 104.1069 ns in volts. The share of the decision at tau is ((dd - de) - the dv = 0 difference) over the same of the row at 98.3069 ns. .param vg=0.742 .param tp=14n .param tau=1 diff --git a/specs/analog/decks/sar_kick_cnode_moved.cir b/specs/analog/decks/sar_kick_cnode_moved.cir new file mode 100644 index 0000000000..8d8a059a51 --- /dev/null +++ b/specs/analog/decks/sar_kick_cnode_moved.cir @@ -0,0 +1,77 @@ +A moved bit's drive: the two-port the bottom-plate drivers present at Bit and Bit_n after the bit's decision, both directions (M4e-kick, #10942) +* specs/analog/decks/sar_kick_cnode_moved.cir -- sar_kick_cnode.cir with the bit MOVED instead of idle: the clock CK11 of DAC_control and neg_bottom_plate_logic high (the tgate x2 to VCM +* and x11 between the plates are off; the inverter XM3 / XM4 holds Bit, and the inverter x12 behind the tgate x15 holds Bit_n), the bit's flip-flop forced by Set or Reset, and each plate node +* held 10 mV off its rail, where the plates sit (they kick by tens of millivolts about it). Two cases: dir 0, the P plate UP (Bit at 1.49 V through the pmos, Bit_n at 0.01 V through the +* inverter x12's nmos and the tgate x15's nmos), dir 1, the P plate DOWN (Bit at 0.01 V through the nmos, Bit_n at 1.49 V through two pmos). The operating point gives the on-resistances as 10 mV / i: +* the P plate 1881 ohm up (pmos W 3 um) and 1428 ohm down (nmos W 1 um), the N plate 2862 and 3772 ohm, twice the inverter's: the golden tgate x15 at a rail is one device of the inverter's size +* again. The ac runs at 100 MHz and 300 MHz give, as sar_kick_cnode.cir does, the admittance matrix Y = G + j w C of the two plate nodes of the moved bit, the capacitances its devices add. +* Paths are NOT in this file: IHP_PDK, IHP_OSDI and SAR_GOLDEN_SUBCKTS as in sar_off_base.cir. Run: ngspice -b sar_kick_cnode_moved.cir (ngspice-47, seconds). TEMP 50 as in the golden deck. +* It prints, between BEGIN y and END y, per direction: DIR d, the operating point's currents i(vpb), i(vnb) (A) and then, per frequency, PORT1 / PORT2 with the two printed currents of the +* sources VPB and VNB (A, volts are 1, as in sar_kick_cnode.cir). +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib mos_tt +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerCAP.lib cap_typ +.include $SAR_GOLDEN_SUBCKTS +VDD vdd 0 1.5 +VCM vcm 0 0.75 +VCK ck 0 1.5 +VIN vin 0 0.75 +VCLK clks 0 0 +VST set 0 {vset} +VRS reset 0 {vrst} +XD vdd 0 sar vcm bitp vin ck set reset clks DAC_control +XN vdd 0 ck bitp bitn neg_bottom_plate_logic +VPB bitp 0 {vpb} ac 1 +VNB bitn 0 {vnb} ac 0 +.param vset=0 +.param vrst=0 +.param vpb=1.49 +.param vnb=0.01 +.options temp=50 +.control +pre_osdi $IHP_OSDI/psp103.osdi +pre_osdi $IHP_OSDI/psp103_nqs.osdi +set numdgt=8 +echo BEGIN y +alterparam vset = 0 +alterparam vrst = 1.5 +alterparam vpb = 1.49 +alterparam vnb = 0.01 +reset +op +echo DIR 0 +print i(vpb) i(vnb) +foreach fr 1e8 3e8 + alter vpb ac = 1 + alter vnb ac = 0 + ac lin 1 $fr $fr + echo PORT1 $fr + print i(vpb) i(vnb) + alter vpb ac = 0 + alter vnb ac = 1 + ac lin 1 $fr $fr + echo PORT2 $fr + print i(vpb) i(vnb) +end +alterparam vset = 1.5 +alterparam vrst = 0 +alterparam vpb = 0.01 +alterparam vnb = 1.49 +reset +op +echo DIR 1 +print i(vpb) i(vnb) +foreach fr 1e8 3e8 + alter vpb ac = 1 + alter vnb ac = 0 + ac lin 1 $fr $fr + echo PORT1 $fr + print i(vpb) i(vnb) + alter vpb ac = 0 + alter vnb ac = 1 + ac lin 1 $fr $fr + echo PORT2 $fr + print i(vpb) i(vnb) +end +echo END y +.endc +.end diff --git a/specs/analog/decks/sar_kick_late.cir b/specs/analog/decks/sar_kick_late.cir new file mode 100644 index 0000000000..cca7e12580 --- /dev/null +++ b/specs/analog/decks/sar_kick_late.cir @@ -0,0 +1,139 @@ +The golden SAR ADC's sine run, the comparator's clock edge at six decisions: the tail node and the drain of the input pair before and through the edge (M4e-kick, #10942) +* specs/analog/decks/sar_kick_late.cir -- sar_off_base.cir (the golden SAR ADC deck of uberWAVE, wp2-parser/golden/top.spice, GPL-2.0: fetched at run time, never vendored; its subcircuits +* come from the run-time file SAR_GOLDEN_SUBCKTS, made as in sar_off_base.cir) with nothing changed in the circuit and a different readout: the golden sine (sar_amp 0.6, the first two conversions, +* 1.1 us) and, at the clock edge of six of its 22 decisions, the waves that sar_kick_edge.cir reads at the first decision with the two gates on ideal sources: the tail node net1 and the drain ap of +* the comparator's input pair, and the plates vdp and vdn, 67 samples 0.1 ns apart. The edge is clkc rising through 0.1 V (tcb) and through 0.75 V (tcc); sample k is at (j0 + 2 k) * 50 ps +* of the 50 ps grid that `linearize` makes, j0 = floor(tcb / 50 ps) - 30, that is 1.5 ns to 1.55 ns before the 0.1 V crossing, to 5.05 ns after it. The six are the clkc rises 1, 10, 11 +* (decisions 0, 9 and 10 of the first conversion) and 12, 21, 22 (the same of the second); the first of each conversion has the sine's plates settled for 14 ns, the last two have nine and ten +* bottom plates moved. Like sar_kick_edge.cir this is the comparator's own history (what net1 and ap do before and through the edge), read where it happens, and sar_kick_net.t27 puts it +* through the same charge tables. +* Paths are NOT in this file: IHP_PDK (the IHP-Open-PDK checkout, commit 21133f21), IHP_OSDI (the directory with the compiled psp103.osdi, psp103_nqs.osdi, r3_cmc.osdi, mosvar.osdi, +* cap_cmomi.osdi, cap_cmomf.osdi) and SAR_GOLDEN_SUBCKTS, in the environment. Run: IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b sar_kick_late.cir (ngspice-47, the +* transient of 1.1 us at 50 ps is the long part of it: minutes on a few cores). It prints, between BEGIN late and END late, per window: the decision's clkc rise number rr, the crossing times +* tcb and tcc in seconds, j0, the plates' difference vdp - vdn at the 0.1 V crossing (vpt - vnt, to compare with D of sar_kick.t27's DEC_D), and the four waves n1w, apw, vpw, vnw. +.param dmv=0 +.param amp=0.6 +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib mos_tt +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerCAP.lib cap_typ +.include $SAR_GOLDEN_SUBCKTS +* supplies, trim code A B C = 0 0 0, the common mode, the sampling clock and the differential sine input +VDD4 vdda 0 1.5 +VDD6 vdd 0 1.5 +VDD7 vcm 0 0.75 +V2 c 0 0 +V5 b 0 0 +V6 a 0 0 +VDD1 vss 0 0 +VCMS net2 0 0.75 +VCLKS clks 0 PULSE(0 1.5 0n 2n 2n 80n 500n) +VSP net2 vinp sin({-dmv*0.5e-3} {amp} 62.5k) +VSN vinn net2 sin({-dmv*0.5e-3} {amp} 62.5k) +* the two tracking switches, the comparator, the SAR logic and the clock generator +x1 vdda 0 vinn vdn clks tracking_switch +x2 vdda 0 vinp vdp clks tracking_switch +x3 vocp vocn valid vdp vdn clkc vdd 0 dynamic_comparator_top +x7 ck3 ck11 ck7 ck2 ck6 ck10 ck1 ck9 ck5 ck8 ck4 vdd 0 vdd valid 0 clks SAR_logic +x4 vdd clkc 0 clks valid ck1 a b c clock_gen +* the 4 fF loads the golden top hangs on the top plates and on the control nets +C3 vdp 0 4f +C4 vdn 0 4f +C5 vdp 0 4f +C6 vdn 0 4f +C7 vinp 0 4f +C8 vinn 0 4f +C9 clkc 0 4f +C10 vocp 0 4f +C11 valid 0 4f +C12 vocn 0 4f +C13 clkc 0 4f +C14 clks 0 4f +C15 valid 0 4f +C16 ck1 0 4f +C17 a 0 4f +C18 b 0 4f +C19 c 0 4f +C20 clks 0 4f +C21 valid 0 4f +C22 vdd 0 4f +* the bottom-plate drivers (every stage decides on vocn), then the negated copy of each bit for the N half +x5 vdd bit_10 vocn 0 ck11 vcm out_11 0 0 clks bit_9 vocn ck10 out_10 bit_8 vocn ck9 out_9 bit_7 vocn ck8 out_8 bit_6 vocn ++ ck7 out_7 bit_5 vocn ck6 out_6 bit_4 vocn ck5 out_5 bit_3 vocn ck4 out_4 bit_2 vocn ck3 out_3 bit_1 vocn ck2 out_2 bit_0 vocn ck1 ++ out_1 DAC_control_top +x6 vdd vss ck11 bit_10 bit_10_n neg_bottom_plate_logic +x8 vdd vss ck6 bit_5 bit_5_n neg_bottom_plate_logic +x9 vdd vss ck10 bit_9 bit_9_n neg_bottom_plate_logic +x10 vdd vss ck5 bit_4 bit_4_n neg_bottom_plate_logic +x11 vdd vss ck9 bit_8 bit_8_n neg_bottom_plate_logic +x12 vdd vss ck4 bit_3 bit_3_n neg_bottom_plate_logic +x13 vdd vss ck8 bit_7 bit_7_n neg_bottom_plate_logic +x14 vdd vss ck3 bit_2 bit_2_n neg_bottom_plate_logic +x15 vdd vss ck7 bit_6 bit_6_n neg_bottom_plate_logic +x16 vdd vss ck2 bit_1 bit_1_n neg_bottom_plate_logic +* the deck's own output adder: V(out) = 1.5 V code / 2048 with the bits at 0 / 1.5 V +E1 out net12 out_11 0 0.5 +E2 net12 net11 out_10 0 0.25 +E3 net11 net10 out_9 0 0.125 +E4 net10 net9 out_8 0 0.0625 +E5 net9 net8 out_7 0 0.03125 +E6 net8 net7 out_6 0 0.015625 +E7 net7 net6 out_5 0 0.0078125 +E8 net6 net5 out_4 0 0.00390625 +E9 net5 net4 out_3 0 0.001953125 +E10 net4 net3 out_2 0 0.0009765625 +E11 net3 0 out_1 0 0.00048828125 +* the two capacitive DAC halves: the N half on VDN with the negated bits, the P half on VDP +x17 vdn bit_3_n bit_8_n bit_5_n bit_6_n bit_7_n bit_4_n bit_9_n bit_10_n bit_2_n bit_1_n Diff_cap_DAC +x18 vdp bit_3 bit_8 bit_5 bit_6 bit_7 bit_4 bit_9 bit_10 bit_2 bit_1 Diff_cap_DAC +.ic v(x1.net1)=0 +.ic v(x1.net2)=0 +.nodeset v(x1.net2)=0 +.ic v(x1.net3)=0 +.ic v(x2.net1)=0 +.ic v(x2.net2)=0 +.ic v(x2.net3)=0 +.ic v(x17.net1)=0 +.ic v(x18.net1)=0 +.ic v(vdp)=0 +.ic v(vdn)=0 +.options temp=50 +.options klu +.save v(vdp) v(vdn) v(clkc) v(x3.x1.ap) v(x3.x1.net1) +.control +pre_osdi $IHP_OSDI/psp103.osdi +pre_osdi $IHP_OSDI/psp103_nqs.osdi +pre_osdi $IHP_OSDI/r3_cmc.osdi +pre_osdi $IHP_OSDI/mosvar.osdi +pre_osdi $IHP_OSDI/cap_cmomi.osdi +pre_osdi $IHP_OSDI/cap_cmomf.osdi +set num_threads=2 +tran 0.05n 1.1u +linearize v(clkc) v(x3.x1.net1) v(x3.x1.ap) v(vdp) v(vdn) +set numdgt=8 +set noprintscale +echo BEGIN late +foreach rr 1 10 11 12 21 22 + meas tran tcb when v(clkc)=0.1 rise=$rr + meas tran tcc when v(clkc)=0.75 rise=$rr + meas tran vpt find v(vdp) when v(clkc)=0.1 rise=$rr + meas tran vnt find v(vdn) when v(clkc)=0.1 rise=$rr + let j0 = floor(tcb / 5e-11) - 30 + let n1w = unitvec(67) + let apw = unitvec(67) + let vpw = unitvec(67) + let vnw = unitvec(67) + let k = 0 + while k < 67 + let j = j0 + 2*k + let n1w[k] = v(x3.x1.net1)[j] + let apw[k] = v(x3.x1.ap)[j] + let vpw[k] = v(vdp)[j] + let vnw[k] = v(vdn)[j] + let k = k + 1 + end + echo WINDOW $rr + print tcb tcc j0 vpt vnt + print n1w apw vpw vnw +end +echo END late +.endc +.end diff --git a/specs/analog/decks/sar_kick_late_aperture.cir b/specs/analog/decks/sar_kick_late_aperture.cir new file mode 100644 index 0000000000..c553cedb86 --- /dev/null +++ b/specs/analog/decks/sar_kick_late_aperture.cir @@ -0,0 +1,166 @@ +The comparator's aperture at a late decision of the golden SAR ADC's sine run: how much a step of its input at time tau still matters to the decision, with the plates as the DAC left them (M4e-kick, #10942) +* specs/analog/decks/sar_kick_late_aperture.cir -- sar_kick_late.cir (the golden SAR ADC deck of uberWAVE, wp2-parser/golden/top.spice, GPL-2.0: fetched at run time, never vendored; the +* subcircuits come from the run-time file SAR_GOLDEN_SUBCKTS, made as in sar_off_base.cir) with ONE thing added: a voltage source BSTEP in series between the plate VDP and the gate of the +* comparator's input pair, 0 V except for a step of dv = 1 mV at the time tau (20 ps ramp). The plate still feeds the gate its charge (the source carries the gate current), so the plates +* and the comparator's edge are the circuit's own; only the gate sees the step. The decision is read as the latch's two outputs v(x3.dp) - v(x3.dn) 4.8 ns after the clock edge's 0.1 V +* crossing, and the ratio of the response to a step at tau to the response to a step before the edge (tau = the crossing minus 1 ns) is the share of the decision that the input still +* decides at tau: 1 until the pair begins to lose its gain, 0 once the latch has locked. sar_kick_aperture.cir is the same experiment at the first decision with the gates on ideal sources; +* this is it at a late one, whose net1 is 38 to 46 mV lower at the edge (sar_kick_late.cir) and whose latch starts a different state (sar_kick_net.t27 weighs the plates' kick by it). +* The sine run's decisions are deterministic, so the run reproduces sar_kick_late.cir to the step: the unperturbed run (dv 0, the first LINEAR row) has the same 0.1 V crossing, tcb. The +* variable sar_which picks the decision, the way sar_off_base.cir picks its input: 0 is the first conversion's tenth decision (clkc rise 10, tcb = 405.4033 ns, nine plates moved), 1 the +* second conversion's (clkc rise 21, tcb = 912.8183 ns). The decisions with ten plates moved (rise 11 and 22) have a net1 within 0.4 mV of these and the same aperture is used for them. +* Paths are NOT in this file: IHP_PDK (the IHP-Open-PDK checkout, commit 21133f21), IHP_OSDI (the directory with the compiled psp103.osdi, psp103_nqs.osdi, r3_cmc.osdi, mosvar.osdi, +* cap_cmomi.osdi, cap_cmomf.osdi) and SAR_GOLDEN_SUBCKTS, and sar_which, in the environment. Run: sar_which=0 IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b +* sar_kick_late_aperture.cir (ngspice-47; 20 transients of 0.41 us (which 0) or 0.92 us (which 1) at 50 ps: minutes each on two cores). It prints, between BEGIN aperture and END aperture, +* the rows LINEAR dv (the step at the crossing minus 1 ns, dv = 0, 0.5 mV and 2 mV), then APERTURE tau (the step at the crossing minus 1 ns, then at 3.0 ns + 0.125 ns j after it, dv = 1 mV) +* with dd = v(x3.dp) and de = v(x3.dn) at the read instant, in volts. +.param dmv=0 +.param amp=0.6 +.param tau=1 +.param dv=0 +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib mos_tt +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerCAP.lib cap_typ +.include $SAR_GOLDEN_SUBCKTS +* supplies, trim code A B C = 0 0 0, the common mode, the sampling clock and the differential sine input +VDD4 vdda 0 1.5 +VDD6 vdd 0 1.5 +VDD7 vcm 0 0.75 +V2 c 0 0 +V5 b 0 0 +V6 a 0 0 +VDD1 vss 0 0 +VCMS net2 0 0.75 +VCLKS clks 0 PULSE(0 1.5 0n 2n 2n 80n 500n) +VSP net2 vinp sin({-dmv*0.5e-3} {amp} 62.5k) +VSN vinn net2 sin({-dmv*0.5e-3} {amp} 62.5k) +* the two tracking switches, the comparator, the SAR logic and the clock generator +x1 vdda 0 vinn vdn clks tracking_switch +x2 vdda 0 vinp vdp clks tracking_switch +x3 vocp vocn valid vdpc vdn clkc vdd 0 dynamic_comparator_top +* the one addition: the step of the aperture experiment, in series between the plate and the comparator's gate +BSTEP vdpc vdp V = {dv}*min(max((time-{tau})/20p,0),1) +x7 ck3 ck11 ck7 ck2 ck6 ck10 ck1 ck9 ck5 ck8 ck4 vdd 0 vdd valid 0 clks SAR_logic +x4 vdd clkc 0 clks valid ck1 a b c clock_gen +* the 4 fF loads the golden top hangs on the top plates and on the control nets +C3 vdp 0 4f +C4 vdn 0 4f +C5 vdp 0 4f +C6 vdn 0 4f +C7 vinp 0 4f +C8 vinn 0 4f +C9 clkc 0 4f +C10 vocp 0 4f +C11 valid 0 4f +C12 vocn 0 4f +C13 clkc 0 4f +C14 clks 0 4f +C15 valid 0 4f +C16 ck1 0 4f +C17 a 0 4f +C18 b 0 4f +C19 c 0 4f +C20 clks 0 4f +C21 valid 0 4f +C22 vdd 0 4f +* the bottom-plate drivers (every stage decides on vocn), then the negated copy of each bit for the N half +x5 vdd bit_10 vocn 0 ck11 vcm out_11 0 0 clks bit_9 vocn ck10 out_10 bit_8 vocn ck9 out_9 bit_7 vocn ck8 out_8 bit_6 vocn ++ ck7 out_7 bit_5 vocn ck6 out_6 bit_4 vocn ck5 out_5 bit_3 vocn ck4 out_4 bit_2 vocn ck3 out_3 bit_1 vocn ck2 out_2 bit_0 vocn ck1 ++ out_1 DAC_control_top +x6 vdd vss ck11 bit_10 bit_10_n neg_bottom_plate_logic +x8 vdd vss ck6 bit_5 bit_5_n neg_bottom_plate_logic +x9 vdd vss ck10 bit_9 bit_9_n neg_bottom_plate_logic +x10 vdd vss ck5 bit_4 bit_4_n neg_bottom_plate_logic +x11 vdd vss ck9 bit_8 bit_8_n neg_bottom_plate_logic +x12 vdd vss ck4 bit_3 bit_3_n neg_bottom_plate_logic +x13 vdd vss ck8 bit_7 bit_7_n neg_bottom_plate_logic +x14 vdd vss ck3 bit_2 bit_2_n neg_bottom_plate_logic +x15 vdd vss ck7 bit_6 bit_6_n neg_bottom_plate_logic +x16 vdd vss ck2 bit_1 bit_1_n neg_bottom_plate_logic +* the deck's own output adder: V(out) = 1.5 V code / 2048 with the bits at 0 / 1.5 V +E1 out net12 out_11 0 0.5 +E2 net12 net11 out_10 0 0.25 +E3 net11 net10 out_9 0 0.125 +E4 net10 net9 out_8 0 0.0625 +E5 net9 net8 out_7 0 0.03125 +E6 net8 net7 out_6 0 0.015625 +E7 net7 net6 out_5 0 0.0078125 +E8 net6 net5 out_4 0 0.00390625 +E9 net5 net4 out_3 0 0.001953125 +E10 net4 net3 out_2 0 0.0009765625 +E11 net3 0 out_1 0 0.00048828125 +* the two capacitive DAC halves: the N half on VDN with the negated bits, the P half on VDP +x17 vdn bit_3_n bit_8_n bit_5_n bit_6_n bit_7_n bit_4_n bit_9_n bit_10_n bit_2_n bit_1_n Diff_cap_DAC +x18 vdp bit_3 bit_8 bit_5 bit_6 bit_7 bit_4 bit_9 bit_10 bit_2 bit_1 Diff_cap_DAC +.ic v(x1.net1)=0 +.ic v(x1.net2)=0 +.nodeset v(x1.net2)=0 +.ic v(x1.net3)=0 +.ic v(x2.net1)=0 +.ic v(x2.net2)=0 +.ic v(x2.net3)=0 +.ic v(x17.net1)=0 +.ic v(x18.net1)=0 +.ic v(vdp)=0 +.ic v(vdn)=0 +.options temp=50 +.options klu +.save v(vdp) v(vdn) v(clkc) v(x3.dp) v(x3.dn) +.control +pre_osdi $IHP_OSDI/psp103.osdi +pre_osdi $IHP_OSDI/psp103_nqs.osdi +pre_osdi $IHP_OSDI/r3_cmc.osdi +pre_osdi $IHP_OSDI/mosvar.osdi +pre_osdi $IHP_OSDI/cap_cmomi.osdi +pre_osdi $IHP_OSDI/cap_cmomf.osdi +set num_threads=2 +set numdgt=8 +set noprintscale +echo BEGIN aperture +if $sar_which = 0 + foreach dvv 0 0.0005 0.002 + alterparam tau = 404.4033n + alterparam dv = $dvv + reset + tran 0.05n 410.5n + meas tran dd find v(x3.dp) at=410.2033n + meas tran de find v(x3.dn) at=410.2033n + meas tran tcb when v(clkc)=0.1 rise=10 + echo LINEAR $dvv + print dd de tcb + end + foreach tauv 404.4033n 408.4033n 408.5283n 408.6533n 408.7783n 408.9033n 409.0283n 409.1533n 409.2783n 409.4033n 409.5283n 409.6533n 409.7783n 409.9033n 410.0283n 410.1533n 410.2783n + alterparam tau = $tauv + alterparam dv = 0.001 + reset + tran 0.05n 410.5n + meas tran dd find v(x3.dp) at=410.2033n + meas tran de find v(x3.dn) at=410.2033n + echo APERTURE $tauv + print dd de + end +else + foreach dvv 0 0.0005 0.002 + alterparam tau = 911.8183n + alterparam dv = $dvv + reset + tran 0.05n 917.9n + meas tran dd find v(x3.dp) at=917.6183n + meas tran de find v(x3.dn) at=917.6183n + meas tran tcb when v(clkc)=0.1 rise=21 + echo LINEAR $dvv + print dd de tcb + end + foreach tauv 911.8183n 915.8183n 915.9433n 916.0683n 916.1933n 916.3183n 916.4433n 916.5683n 916.6933n 916.8183n 916.9433n 917.0683n 917.1933n 917.3183n 917.4433n 917.5683n 917.6933n + alterparam tau = $tauv + alterparam dv = 0.001 + reset + tran 0.05n 917.9n + meas tran dd find v(x3.dp) at=917.6183n + meas tran de find v(x3.dn) at=917.6183n + echo APERTURE $tauv + print dd de + end +end +echo END aperture +.endc +.end diff --git a/specs/analog/sar_kick_net.t27 b/specs/analog/sar_kick_net.t27 new file mode 100644 index 0000000000..038408ccd8 --- /dev/null +++ b/specs/analog/sar_kick_net.t27 @@ -0,0 +1,1925 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/analog/sar_kick_net.t27 -- the SAR ADC comparator's clock-edge kick on the two top plates, derived: a 34-node capacitor network, the drivers' resistances, the comparator's charge pulse from charge tables, M1's fixed-step solve +; M4e-kick of the SAR ADC milestone (#10942, part of #10888 and #10818, epic #10486). sar_kick.t27 (#10941) MEASURED where the converter's decision offset comes from: when the comparator's clock rises, its +; input pair pushes charge into the two top plates VDP and VDN, they fall by 22.7 and 30.2 mV by clkc = 0.75 V, and the 7.5 mV between them is what the latch reads, because the N half's bottom plates are driven +; through one transmission gate more than the P half's. The kick was measured there, not derived. This file derives it, from the netlist, the PSP103 tables and ngspice measurements on committed decks, with no +; number fitted to a code or to a kick it predicts (FR-001). +; - +; THE CHAIN. (1) THE PULSE. The input pair (XM3, XM4: 36 sg13_lv_nmos W 1 um in parallel) has its gate on the plate, its source on the tail node net1 and its drain on ap (an). When clkc rises net1 falls from 0.65 V +; to 0.04 V and ap from 1.5 V to 0.05 V (sar_kick_edge.cir prints both, 67 samples 0.1 ns apart, the two gates on ideal sources). The gate charge Q_G(Vg, Vs, Vd) of one device at 50 C with the bulk on ground +; comes from three tables of 25 x 25 points on three gate levels (sar_kick_qg.cir: M3c's recipe with the comparator's bulk and temperature), read bilinearly by M3a's reader along the two waves: the gates take +; dQ_G(t) from the plates. At clkc = 0.75 V that is 26.93 fC from the tables against 26.90 fC that ngspice integrated from the gate current (0.1 percent); M3c's own table (bulk at the source, 27 C) gives 36.4 fC, +; 35 percent more, which is why a table of its own was made. +; (2) THE PLATES. Each half is a capacitor network: 63 units of 122.94 fF (sar_cdac.t27: 31 on the top plate through its bottom plates, 31 on net1 behind the one-unit bridge), 12 fF of the golden top level and the +; tracking switch on each top plate, and the pair's gate capacitance (dQ_G / dVg across the tables: 59 fF at the 0.1 V crossing, 110 fF averaged over the pulse by the charge each interval puts in, because the +; matrix of M1's step is one constant matrix). Each bottom plate is held by its driver. An IDLE bit's drivers are the two-port ngspice measured in sar_kick_cnode.cir: the tgate x2 to VCM (2586 ohm) and x11 +; between Bit and Bit_n (2587 ohm), and the capacitances the devices add, 25.6, 12.8 and 3.2 fF at 100 MHz. A MOVED bit's P plate is held by DAC_control's inverter, whose on-resistance (1419 ohm nmos, 1870 ohm +; pmos) M3b's 50 C current tables give within 0.6 percent of ngspice's (sar_off_ron.cir), and its N plate by the inverter and one tgate more, at a rail one device again: twice that (sar_kick_cnode_moved.cir +; measures the moved bit's operating point and two-port: 1881 / 1428 ohm and 3772 / 2862 ohm, within 1 percent of those, and the capacitances its devices add, 50 and 41 fF at the P plate's node, 11 and 19 fF +; at the N plate's, 2 fF between, which the network takes). The tables cannot give the tgate at VCM: a table is made with the source and bulk together and the golden tgate's nmos has its source at 0.75 V over a +; grounded bulk, so they read it 30 percent too stiff (1813 against 2586 ohm; control 3). The network is 34 nodes: VDP, VDN, their two net1 nodes and three per bit (Bit_k, Bit_k_n, the P plate behind its series +; resistor). The pulse goes into both top plates. M1's own matrix derivation is capped at 5 unknowns, so M = A^-1 C/dt and N = A^-1 b of x' = M x + N u are derived here by Gauss-Jordan elimination with partial +; pivoting (the solve's residual is tested), and every step of the run is M1's `step` on their Q.24 words, called by name; it follows the f64 run of the same method to 1.2 uV. dt = 10 ps, converged (control 5). +; (3) THE DECISION. The latch does not read VDP - VDN at one instant. sar_kick_aperture.cir steps the gate of VDP by 1 mV at a time tau and reads the latch's output difference 4.8 ns after the 0.1 V crossing; +; the ratio to the response of a step before the edge is the share of the decision that the input still decides at tau: 1.00 until 3 ns after the crossing, 0.60 at 4.0 ns, 0.07 at 4.5 ns, 0 from 4.75 ns (20 +; transients, linear in the step to 0.3 percent). The decision reads the change of VDP - VDN since the crossing weighted by those shares, plus the latch's own 0.153 mV (M4b-psp): the offset. +; - +; THE RESULT, first decision, every plate idle. ngspice: sar_kick.t27. A: the issue's model, one charge pulse for the pair. B: the extension the three gate-level tables allow, each plate's device taking the charge +; of its own gate level (0.742 V + D / 2 for VDP, - D / 2 for VDN, D the VDP - VDN of the experiment; the charge rises 81 fC per volt of gate), the offsets at the D the decision flips at (the fixed point +; d = -off: the measured flip is no input). +; S1 the golden circuit, dVDP / dVDN at clkc 0.75 V -22.7 / -30.2 mV A -22.4 / -29.5 mV 1.3 / 2.0 percent (criterion 10) B -22.3 / -29.7 mV 1.7 / 1.5 percent +; S2 both halves ideal, dVDP = dVDN -6.87 / -6.88 mV A -6.71 / -6.71 mV 2.2 percent (5), differ 0 (0.1 mV) B -6.71 / -6.72 mV 2.3 percent +; (the network is then a capacitor divider: the closed form agrees to 0.5 percent) +; S3 N half ideal, dVDP - dVDN -20.5 mV A -19.1 mV 6.6 percent (15) B -19.9 mV 2.6 percent +; P half ideal +35.9 mV A +33.0 mV 8.1 percent (15) B +35.0 mV 2.7 percent +; S4 P plates behind 1300 / 2592 / 3900 ohm, offset +4.09 / -0.89 / -4.19 mV A +4.06 / -0.55 / -4.77 mV (within 0.6 mV, 1) B +4.19 / -0.57 / -4.95 mV +; it crosses the latch's 0.153 mV at 2320 ohm A 2386 ohm (asked 2200 .. 2450) +; the seven offsets (SC 2, within 1 mV): ngspice base 9.42, ideal switches 9.19, comparator isolated 0.03, N ideal -25.79, P ideal +43.87, both ideal 0.27, 2592 ohm -0.89 +; A 9.34, 9.34 (the switch is not in the network), 0.15 (no pulse: the latch's own), -24.86, +43.03, 0.15, -0.55: all within 1 mV +; B 9.60, 9.60, 0.15, -25.71, +45.02, 0.16, -0.57: the P half ideal row is 1.15 mV above ngspice's, 0.15 mV past the 1 mV; the rest within 0.4 mV +; the order P ideal > golden > symmetric = comparator isolated > N ideal holds in both; the signs and sizes are the measurement's. B is closer on every kick (the three differentials 0.8, 2.6, 2.7 percent against +; 4.1, 6.6, 8.1) and one row further on the offset: the offset is the kick read through the aperture, and the model's ratio of the one to the other is 1.30 against ngspice's 1.26. A is the issue's model and +; the default of everything below; B is stated beside it wherever it differs. +; - +; THE CONTROLS, each a test that passes by seeing the failure: +; 1. no pulse: the plates stay put and the offset is the latch's 0.153 mV. +; 2. M3c's table for the pulse (35 percent more charge): the plates fall 21 percent too far, S1 fails its 10 percent. +; 3. the tgate from M3b's tables (no body effect, 1.43 times too stiff a drive): S1 fails by 17 and 19 percent, the base offset is 2 mV low. +; 4. the idle bit's two-port capacitances left out: the one-half-ideal rows move by 1.7 and 2.7 mV, the 2592 ohm row misses ngspice's by 1.02 mV. +; 5. dt = 100 ps (one step per sample): the one-half-ideal rows move by 0.55 and 0.87 mV and miss the 1 mV criterion; dt = 5 ps moves them by 0.03 mV. +; 6. (late decisions) the first decision's edge and aperture, as the first lane read them: 7.09, 7.09, 6.77, 6.77 mV, the first conversion's tenth 0.43 mV off; the late edge with the first decision's +; aperture: 6.98, 7.01, 6.80, 6.77 (the aperture's timing is worth 0.1 to 0.2 mV: 0.6 mV per 0.1 ns); the bits' capacitances left out: +0.11, +0.11, +0.05, +0.05 mV; the first conversion's plates under the +; second's edge and aperture, and the reverse: 0.3 mV from the plates that belong to them. +; - +; THE LATE DECISIONS (S5). The converter's codes are decided by the last decisions (sar_kick.t27: 6.66 and 6.90 mV at decisions 9 and 10, from 28 decisions each). The first lane read them with the first +; decision's pulse and aperture and missed by 0.2 to 0.6 mV, for want of the late decision's own edge. sar_kick_late.cir reads the golden sine's own clock edge at its tenth and eleventh decisions (net1 38 to 46 mV +; below the first decision's at the window start, 0.614 and 0.622 V against 0.660 V; the pair takes 27.2 to 27.6 fC between 0.1 V and 0.75 V, its gate capacitance 111 to 113 fF; the plates' common mode 0.741 V in +; the first conversion, 0.749 V in the second); sar_kick_late_aperture.cir measures the aperture there (the latch has 0.68 and 0.71 of the first decision's gain at the read instant and its share closes earlier: +; 0.555 at 4.0 ns against 0.601); sar_kick_cnode_moved.cir the moved bit. The decision with nine (ten) plates moved is the network with those plates held by their inverters (the P plate down for a decision of +; 1 and up for 0, from the sine's own decisions, the N plate the other way behind the inverter and the tgate) and the idle one by its two-port, driven by its own edge and aperture. Offsets in mV: +; A one pulse B own gate levels measured (sar_kick.t27) +; first conversion, decision 9 6.83 7.00 6.658 +- 0.05, one threshold for all 28 paths +; first conversion, decision 10 6.86 7.08 6.895, 6.85 to 7.06 by the bit path +; second conversion, decision 9 6.63 6.90 6.658 +; second conversion, decision 10 6.60 6.82 6.895 +; within the issue's 0.2 mV 3 of 4 2 of 4 +; S5 is NOT met, by either: A misses the second conversion's decision 10 by 0.29 mV (and sits 0.25 mV under the lowest the paths allow), B the two decisions 9 by 0.34 and 0.24 mV. The mean of A's four, +; 6.731 mV, is 0.045 mV under the 6.776 the converter model takes; sar_adc.t27 is NOT switched to the derived value (the success criterion asks for S5 first) and is untouched. What the network does not have, and +; the next gap: (1) the bit path. The measured offset at decision 9 is one number over all 28 paths (0.05 mV); the network's two paths are 0.2 mV apart and its plates under the other path's edge 0.3 mV: the +; up and down drives of a moved plate (1881 and 1428 ohm for the P plate, 3772 and 2862 for the N plate) matter more in the model than in the circuit, where the inverter's output sits within tens of +; millivolts of its rail through the kick and the tables read it at Vds -> 0. (2) decision 10 against 9: the measured offset is 0.24 mV higher at the tenth, the network's the same to 0.03 mV (the one-unit plate +; that moves in between). A reading of the in-situ moved bit's drive during the kick (not at 10 mV) and of the plate's own settling is the experiment that would separate them. +; - +; WHAT IS NOT DERIVED (FR-003). With ideal plates the first plate step reads a top-plate load Cp of 0.582 units of the 9 um capacitor; the network's own is the explicit 12 fF plus the pair's gate capacitance at +; the pre-edge bias from the tables, 59.2 fF: 0.579 units, within 1 percent, a test. The golden circuit reads 0.647 and the P plates behind 1300, 2592 and 3900 ohm read 0.62, 0.69 and 0.85, and a raised and a lowered P +; plate differ by 2.85 mV (sar_kick.t27). The network has the mechanism (the biggest plate settles later the softer its driver, and the N half's, one tgate softer, later still; the raised case's slowest path is 2838 +; ohm and the lowered case's 3740, which gives the sign of the 2.85 mV, not tested here) and no number for it: the step is read a time after the plate moves that the pinned data do not hold (the inverter's own +; switching, the time to the second clkc edge). It is the next gap. +; - +; WHAT A PASS SAYS. The pinned data are the numbers the decks printed (six folds); the tables' pulse is ngspice's to 0.1 percent and the table of M3c is not; the drivers' resistances are the tables' and the +; measured two-ports', within the stated percent of ngspice's own measurements; the network's solve is a solve and M1's words follow the f64 run to 1.2 uV; with ideal plates the network is the capacitor divider; +; the derived kicks are within the issue's tolerances of ngspice's (S1 to S4) and the seven first-decision offsets within 1 mV, in the measured order; the controls fail the way they are said to; the late decisions +; are derived with their own edge and aperture to 0.17, 0.03, 0.03 and 0.29 mV (A), and S5's miss is pinned with its numbers. +; WHAT IT DOES NOT SAY. Nothing about speed, power, noise, mismatch, another corner or another temperature, and nothing about decks beyond the numbers pinned here: this file runs no ngspice. S1 to S4 read the first +; decision's edge (ideal gate sources, plates settled for 14 ns), which is what the offsets were measured on; the tracking switch's own charge injection is not in the network (the ideal-switch row is the base row: +; ngspice found the two 0.23 mV apart); the switch's off-state junction capacitance (about 0.3 fF) is not in the 12 fF; for A the gate charge is read at one gate level for both plates (B reads each at its own). +; The windows of the sine run's first decisions are pinned for their D (a check of the run) and one test: window 0 (rise 1, plates 31 mV apart, read with the table at one gate level for both: 9.97 mV against +; the edge deck's 9.34), window 3 (rise 12, plates 269 mV apart, beyond the tables' gate range) not at all. +; ASCII only, English only. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module AnalogSarKickNet; + +use analog::linear::{step, q_sm, sm_f64, fabs64, fold, SCALE}; +use analog::mos_table::{idf}; +use analog::mos_psp103_50c_data::{TT50_N, TT50_P}; +use analog::mos_psp103_data::{TT_QG_N, fold_arr}; +use analog::sar_cdac::{PLATES, UNITS, SIDE}; +use analog::sar_kick::{V_BASE, V_IDEALSW, V_NOKICK, V_PLATES, V_NPLATE, V_PPLATE, LATCH_FLIP_V, KICK_P, KICK_N, FLIP_LO, RS_LO, RS_OHM, RON_TG, RON_INVP, RON_INVN, NRS, first_off, kick_diff, rs_off, rs_zero_ohm, cp_of_step, step_mean, late_thr, off_late_v, DEC_D, LD_BIT, RS_STEP_UP, RS_STEP_DN, STEP_UP, STEP_DN}; + +pub const KIND : str = "analog-cell"; +pub const ID : str = "analog/sar_kick_net"; +pub const REFERENCE : str = "uberWAVE wp2-parser/golden/top.spice (read at run time, GPL-2.0, not vendored); specs/analog/decks/sar_kick_*.cir"; + +; --- the units of the network --------------------------------------------------------------------------------------------------------- +; Time in ns, capacitance in fF, charge in fC, resistance in ohm, voltage in V. Then a conductance in microsiemens is 1.0e6 / R and C / dt is in microsiemens too, +; a current in fC / ns is in microamperes, and (G + C / dt) x = ... is in volts. The unknowns are the node voltages as deviations from the pre-edge state. + +/// Nodes of the network: the two top plates and their two net1 nodes, then three per bottom plate (Bit_k, Bit_k_n, the P plate behind its series resistor). +pub const NMAX : usize = 34; +pub const NSQ : usize = 1156; +/// The work matrix [A | C / dt | b]: NMAX rows of 2 NMAX + 1 columns. +pub const WA : usize = 69; +pub const NAUG : usize = 2346; +/// The ground, as a node index. +pub const GND : usize = 99; +/// A conductance that joins two nodes that are one node (1 mOhm), microsiemens. +pub const G_SHORT : f64 = 1000000000.0; + +/// One cap_cmim unit (9 um x 9 um), fF (M4c, sar_switch.t27: 122.94 fF at 9 x 9). +pub const C_UNIT_FF : f64 = 122.94; +/// What the golden netlist hangs on each top plate besides the DAC and the comparator: C3 + C5 (C4 + C6) of the top level and C2 of the tracking switch, 4 fF each. +pub const C_EXPLICIT_FF : f64 = 12.0; +/// The input pair XM3, XM4 is 36 sg13_lv_nmos W 1.0 um in parallel. +pub const M_PAIR : f64 = 36.0; +/// One gate-charge table unit in fC: 500 fF * 2^-24 V. +pub const QG_UNIT_FC : f64 = 0.0000298023223876953125; +/// The gate step between the three charge tables, V, and the level of the middle one. +pub const QG_DVG : f64 = 0.05; +pub const VG_PLATES : f64 = 0.742; +/// The gate step between neighbouring charge tables, V. +pub const QG_DVG_STEP : f64 = 0.025; + +/// Drive modes of a bottom plate, P half and N half alike. +pub const DR_IDLE : u8 = 0; +pub const DR_UP : u8 = 1; +pub const DR_DOWN : u8 = 2; +pub const DR_IDEAL : u8 = 3; +pub const DR_MIRROR : u8 = 4; + +/// The waveform: 67 samples, 0.1 ns apart from 97.8 ns; clkc crosses 0.1 V at 99.3069 ns (sample 15.069) and 0.75 V at 103.118 ns (sample 53.18). +pub const NS : usize = 67; +pub const SAMPLE_NS : f64 = 0.1; +pub const T0_NS : f64 = 97.8; +pub const T01_NS : f64 = 99.3069; +pub const T075_NS : f64 = 103.118; +pub const T01_K : f64 = 15.069; +pub const T075_K : f64 = 53.18; +/// Steps of the network per sample: 10, so dt = 10 ps. +pub const SUB : usize = 10; + +; --- BEGIN PINNED DATA (ngspice-47 + PSP103, IHP-Open-PDK 21133f21, mos_tt, 50 C; the decks specs/analog/decks/sar_kick_*.cir, read 2026-10-19) ---------------------- +/// The gate charge of ONE sg13_lv_nmos (W 1.0 um, L 0.45 um), bulk on ground, 50 C, the gate at 0.717 V, relative to the origin (Vg 0.742 V, Vs = Vd = 0) plus 2^21, in units of 500 fF * 2^-24 V: +/// entry ix * 25 + iy at Vs = ix * 31.25 mV, Vd = iy * 62.5 mV (sar_kick_qg.cir, index jg * 625 + ix * 25 + iy). +pub const QG_LO : [625]u32 = [ + 2092343, 2086583, 2081287, 2076670, 2072941, 2070095, 2067899, 2066084, 2064471, 2062965, 2061523, 2060121, 2058748, 2057398, 2056066, 2054750, 2053450, 2052163, 2050889, 2049627, 2048378, 2047140, 2045914, 2044699, 2043494, + 2089415, 2083523, 2078065, 2073239, 2069301, 2066323, 2064080, 2062263, 2060662, 2059171, 2057742, 2056352, 2054988, 2053646, 2052321, 2051012, 2049717, 2048436, 2047167, 2045910, 2044665, 2043431, 2042208, 2040997, 2039796, + 2086583, 2080556, 2074943, 2069914, 2065749, 2062613, 2060312, 2058493, 2056905, 2055430, 2054014, 2052635, 2051281, 2049947, 2048630, 2047327, 2046038, 2044762, 2043498, 2042245, 2041005, 2039775, 2038556, 2037348, 2036151, + 2083865, 2077691, 2071926, 2066703, 2062300, 2058977, 2056602, 2054778, 2053206, 2051746, 2050345, 2048977, 2047633, 2046307, 2044997, 2043701, 2042418, 2041147, 2039887, 2038640, 2037403, 2036177, 2034962, 2033758, 2032564, + 2081287, 2074943, 2069021, 2063615, 2058971, 2055428, 2052959, 2051127, 2049571, 2048128, 2046741, 2045385, 2044050, 2042733, 2041430, 2040140, 2038862, 2037596, 2036342, 2035099, 2033866, 2032645, 2031433, 2030233, 2029042, + 2078878, 2072339, 2066243, 2060658, 2055781, 2051988, 2049396, 2047551, 2046011, 2044586, 2043212, 2041867, 2040542, 2039233, 2037937, 2036653, 2035381, 2034120, 2032871, 2031632, 2030404, 2029186, 2027979, 2026781, 2025594, + 2076670, 2069914, 2063615, 2057843, 2052746, 2048683, 2045930, 2044063, 2042539, 2041132, 2039772, 2038439, 2037123, 2035821, 2034532, 2033254, 2031987, 2030732, 2029487, 2028252, 2027028, 2025815, 2024611, 2023417, 2022233, + 2074688, 2067706, 2061175, 2055194, 2049883, 2045547, 2042589, 2040685, 2039177, 2037787, 2036440, 2035117, 2033810, 2032515, 2031233, 2029961, 2028700, 2027449, 2026209, 2024978, 2023758, 2022548, 2021348, 2020158, 2018977, + 2072941, 2065749, 2058971, 2052746, 2047214, 2042617, 2039411, 2037446, 2035951, 2034577, 2033244, 2031931, 2030631, 2029344, 2028067, 2026801, 2025545, 2024299, 2023063, 2021837, 2020621, 2019415, 2018218, 2017031, 2015854, + 2071418, 2064057, 2057050, 2050555, 2044772, 2039928, 2036450, 2034390, 2032903, 2031545, 2030223, 2028919, 2027627, 2026346, 2025076, 2023815, 2022563, 2021322, 2020090, 2018868, 2017656, 2016453, 2015260, 2014076, 2012901, + 2070095, 2062613, 2055428, 2048683, 2042617, 2037521, 2033777, 2031580, 2030094, 2028749, 2027438, 2026141, 2024856, 2023581, 2022316, 2021060, 2019813, 2018575, 2017347, 2016129, 2014920, 2013720, 2012530, 2011349, 2010178, + 2068934, 2061381, 2054083, 2047154, 2040819, 2035449, 2031468, 2029106, 2027605, 2026271, 2024968, 2023679, 2022399, 2021129, 2019868, 2018616, 2017373, 2016139, 2014915, 2013700, 2012494, 2011297, 2010110, 2008931, 2007762, + 2067899, 2060312, 2052959, 2045930, 2039411, 2033777, 2029578, 2027064, 2025532, 2024204, 2022908, 2021624, 2020350, 2019083, 2017826, 2016577, 2015337, 2014106, 2012885, 2011672, 2010468, 2009274, 2008088, 2006912, 2005745, + 2066958, 2059362, 2051992, 2044928, 2038326, 2032522, 2028130, 2025507, 2023936, 2022610, 2021319, 2020039, 2018767, 2017504, 2016249, 2015002, 2013764, 2012535, 2011315, 2010104, 2008902, 2007709, 2006526, 2005351, 2004185, + 2066084, 2058493, 2051127, 2044063, 2037446, 2031580, 2027064, 2024370, 2022773, 2021444, 2020156, 2018878, 2017608, 2016346, 2015092, 2013847, 2012610, 2011383, 2010164, 2008953, 2007752, 2006560, 2005377, 2004203, 2003038, + 2065259, 2057680, 2050328, 2043279, 2036672, 2030802, 2026243, 2023504, 2021895, 2020563, 2019275, 2017998, 2016729, 2015468, 2014215, 2012971, 2011735, 2010507, 2009289, 2008079, 2006878, 2005687, 2004504, 2003331, 2002166, + 2064471, 2056905, 2049571, 2042539, 2035951, 2030094, 2025532, 2022773, 2021159, 2019824, 2018536, 2017260, 2015991, 2014730, 2013478, 2012233, 2010997, 2009770, 2008552, 2007343, 2006142, 2004951, 2003768, 2002595, 2001430, + 2063708, 2056158, 2048840, 2041827, 2035257, 2029415, 2024861, 2022098, 2020480, 2019145, 2017857, 2016580, 2015312, 2014051, 2012799, 2011554, 2010318, 2009091, 2007873, 2006664, 2005463, 2004272, 2003090, 2001916, 2000752, + 2062965, 2055430, 2048128, 2041132, 2034577, 2028749, 2024204, 2021444, 2019824, 2018489, 2017200, 2015924, 2014655, 2013395, 2012142, 2010898, 2009662, 2008435, 2007217, 2006008, 2004807, 2003616, 2002434, 2001260, 2000096, + 2062238, 2054716, 2047430, 2040448, 2033907, 2028091, 2023554, 2020798, 2019178, 2017842, 2016554, 2015277, 2014009, 2012748, 2011496, 2010251, 2009016, 2007789, 2006570, 2005361, 2004161, 2002969, 2001787, 2000614, 1999449, + 2061523, 2054014, 2046741, 2039772, 2033244, 2027438, 2022908, 2020156, 2018536, 2017200, 2015911, 2014635, 2013366, 2012106, 2010853, 2009609, 2008374, 2007146, 2005928, 2004719, 2003519, 2002327, 2001145, 1999971, 1998807, + 2060818, 2053321, 2046059, 2039103, 2032585, 2026788, 2022265, 2019515, 2017897, 2016561, 2015272, 2013995, 2012727, 2011467, 2010214, 2008970, 2007734, 2006507, 2005289, 2004079, 2002879, 2001688, 2000505, 1999332, 1998167, + 2060121, 2052635, 2045385, 2038439, 2031931, 2026141, 2021624, 2018878, 2017260, 2015924, 2014635, 2013358, 2012090, 2010829, 2009577, 2008333, 2007097, 2005870, 2004652, 2003442, 2002242, 2001051, 1999868, 1998695, 1997530, + 2059432, 2051955, 2044715, 2037779, 2031279, 2025498, 2020986, 2018242, 2016624, 2015289, 2014000, 2012723, 2011455, 2010194, 2008942, 2007697, 2006462, 2005235, 2004016, 2002807, 2001607, 2000415, 1999233, 1998059, 1996895, + 2058748, 2051281, 2044050, 2037123, 2030631, 2024856, 2020350, 2017608, 2015991, 2014655, 2013366, 2012090, 2010821, 2009561, 2008308, 2007064, 2005828, 2004601, 2003383, 2002174, 2000973, 1999782, 1998600, 1997426, 1996262 +]; +/// The gate charge of ONE sg13_lv_nmos (W 1.0 um, L 0.45 um), bulk on ground, 50 C, the gate at 0.742 V, relative to the origin (Vg 0.742 V, Vs = Vd = 0) plus 2^21, in units of 500 fF * 2^-24 V: +/// entry ix * 25 + iy at Vs = ix * 31.25 mV, Vd = iy * 62.5 mV (sar_kick_qg.cir, index jg * 625 + ix * 25 + iy). +pub const QG_MID : [625]u32 = [ + 2097152, 2091347, 2085972, 2081224, 2077309, 2074267, 2071916, 2069998, 2068320, 2066773, 2065302, 2063878, 2062489, 2061124, 2059780, 2058454, 2057143, 2055847, 2054564, 2053295, 2052038, 2050793, 2049559, 2048337, 2047126, + 2094204, 2088274, 2082747, 2077800, 2073677, 2070487, 2068071, 2066141, 2064472, 2062938, 2061480, 2060069, 2058689, 2057333, 2055997, 2054677, 2053372, 2052082, 2050804, 2049539, 2048286, 2047045, 2045816, 2044597, 2043389, + 2091347, 2085287, 2079615, 2074477, 2070131, 2066769, 2064275, 2062331, 2060672, 2059153, 2057709, 2056309, 2054939, 2053592, 2052263, 2050950, 2049652, 2048367, 2047094, 2045834, 2044585, 2043348, 2042123, 2040908, 2039704, + 2088596, 2082396, 2076580, 2071260, 2066686, 2063124, 2060534, 2058572, 2056924, 2055421, 2053991, 2052604, 2051244, 2049906, 2048584, 2047278, 2045986, 2044706, 2043439, 2042183, 2040939, 2039706, 2038484, 2037273, 2036072, + 2085972, 2079615, 2073649, 2068158, 2063356, 2059568, 2056857, 2054872, 2053234, 2051748, 2050334, 2048958, 2047609, 2046280, 2044966, 2043666, 2042380, 2041106, 2039843, 2038592, 2037353, 2036124, 2034906, 2033698, 2032501, + 2083505, 2076965, 2070835, 2065175, 2060155, 2056117, 2053255, 2051237, 2049612, 2048144, 2046744, 2045381, 2044042, 2042721, 2041415, 2040122, 2038841, 2037573, 2036315, 2035069, 2033833, 2032609, 2031394, 2030190, 2028997, + 2081224, 2074477, 2068158, 2062323, 2057097, 2052796, 2049745, 2047681, 2046068, 2044617, 2043233, 2041883, 2040554, 2039241, 2037942, 2036655, 2035380, 2034117, 2032864, 2031623, 2030391, 2029171, 2027960, 2026760, 2025570, + 2079154, 2072188, 2065650, 2059620, 2054193, 2049632, 2046351, 2044221, 2042617, 2041185, 2039816, 2038477, 2037158, 2035853, 2034561, 2033281, 2032012, 2030753, 2029505, 2028268, 2027041, 2025824, 2024618, 2023421, 2022234, + 2077309, 2070131, 2063356, 2057097, 2051462, 2046653, 2043106, 2040879, 2039282, 2037868, 2036514, 2035186, 2033876, 2032579, 2031293, 2030019, 2028755, 2027502, 2026259, 2025026, 2023803, 2022590, 2021387, 2020194, 2019010, + 2075686, 2068326, 2061320, 2054803, 2048932, 2043887, 2040057, 2037692, 2036094, 2034697, 2033357, 2032040, 2030738, 2029448, 2028169, 2026900, 2025642, 2024393, 2023155, 2021926, 2020707, 2019498, 2018298, 2017108, 2015928, + 2074267, 2066769, 2059568, 2052796, 2046653, 2041364, 2037261, 2034712, 2033100, 2031719, 2030391, 2029084, 2027790, 2026506, 2025233, 2023970, 2022716, 2021472, 2020237, 2019013, 2017797, 2016592, 2015396, 2014209, 2013032, + 2073022, 2065432, 2058093, 2051114, 2044694, 2039133, 2034778, 2032014, 2030368, 2028998, 2027681, 2026382, 2025095, 2023818, 2022550, 2021291, 2020042, 2018802, 2017571, 2016350, 2015138, 2013936, 2012743, 2011559, 2010385, + 2071916, 2064275, 2056857, 2049745, 2043106, 2037261, 2032662, 2029691, 2027985, 2026623, 2025315, 2024023, 2022742, 2021469, 2020206, 2018951, 2017706, 2016469, 2015242, 2014024, 2012815, 2011615, 2010425, 2009243, 2008071, + 2070917, 2063254, 2055801, 2048627, 2041866, 2035803, 2030964, 2027827, 2026047, 2024685, 2023383, 2022097, 2020819, 2019551, 2018291, 2017039, 2015797, 2014563, 2013338, 2012122, 2010915, 2009718, 2008529, 2007350, 2006180, + 2069998, 2062331, 2054872, 2047681, 2040879, 2034712, 2029691, 2026427, 2024590, 2023220, 2021923, 2020640, 2019366, 2018100, 2016842, 2015592, 2014352, 2013119, 2011896, 2010682, 2009477, 2008280, 2007093, 2005915, 2004746, + 2069137, 2061477, 2054026, 2046843, 2040041, 2033851, 2028750, 2025397, 2023530, 2022152, 2020856, 2019575, 2018302, 2017037, 2015781, 2014532, 2013292, 2012061, 2010839, 2009625, 2008421, 2007225, 2006039, 2004861, 2003693, + 2068320, 2060672, 2053234, 2046068, 2039282, 2033100, 2027985, 2024590, 2022707, 2021323, 2020027, 2018747, 2017475, 2016211, 2014955, 2013707, 2012467, 2011237, 2010015, 2008801, 2007597, 2006402, 2005216, 2004039, 2002871, + 2067534, 2059900, 2052479, 2045331, 2038563, 2032398, 2027291, 2023886, 2021993, 2020608, 2019310, 2018030, 2016759, 2015495, 2014239, 2012991, 2011752, 2010522, 2009300, 2008087, 2006883, 2005688, 2004502, 2003325, 2002156, + 2066773, 2059153, 2051748, 2044617, 2037868, 2031719, 2026623, 2023220, 2021323, 2019937, 2018640, 2017360, 2016088, 2014824, 2013569, 2012321, 2011082, 2009851, 2008629, 2007417, 2006213, 2005018, 2003832, 2002655, 2001486, + 2066030, 2058424, 2051035, 2043920, 2037186, 2031051, 2025966, 2022569, 2020672, 2019285, 2017987, 2016707, 2015435, 2014172, 2012916, 2011668, 2010429, 2009199, 2007977, 2006764, 2005560, 2004365, 2003179, 2002002, 2000834, + 2065302, 2057709, 2050334, 2043233, 2036514, 2030391, 2025315, 2021923, 2020027, 2018640, 2017342, 2016061, 2014790, 2013526, 2012271, 2011023, 2009784, 2008553, 2007332, 2006119, 2004915, 2003720, 2002534, 2001357, 2000189, + 2064585, 2057004, 2049642, 2042555, 2035847, 2029735, 2024668, 2021280, 2019386, 2017998, 2016700, 2015420, 2014149, 2012885, 2011629, 2010382, 2009142, 2007912, 2006690, 2005477, 2004273, 2003078, 2001892, 2000715, 1999547, + 2063878, 2056309, 2048958, 2041883, 2035186, 2029084, 2024023, 2020640, 2018747, 2017360, 2016061, 2014781, 2013510, 2012246, 2010990, 2009743, 2008503, 2007273, 2006051, 2004838, 2003634, 2002439, 2001253, 2000076, 1998908, + 2063180, 2055621, 2048281, 2041216, 2034529, 2028435, 2023381, 2020002, 2018110, 2016723, 2015425, 2014144, 2012873, 2011609, 2010353, 2009106, 2007867, 2006636, 2005414, 2004201, 2002997, 2001803, 2000617, 1999440, 1998272, + 2062489, 2054939, 2047609, 2040554, 2033876, 2027790, 2022742, 2019366, 2017475, 2016088, 2014790, 2013510, 2012238, 2010974, 2009718, 2008471, 2007232, 2006001, 2004779, 2003567, 2002363, 2001168, 1999982, 1998805, 1997637 +]; +/// The gate charge of ONE sg13_lv_nmos (W 1.0 um, L 0.45 um), bulk on ground, 50 C, the gate at 0.767 V, relative to the origin (Vg 0.742 V, Vs = Vd = 0) plus 2^21, in units of 500 fF * 2^-24 V: +/// entry ix * 25 + iy at Vs = ix * 31.25 mV, Vd = iy * 62.5 mV (sar_kick_qg.cir, index jg * 625 + ix * 25 + iy). +pub const QG_HI : [625]u32 = [ + 2101985, 2096139, 2090695, 2085829, 2081743, 2078510, 2075997, 2073965, 2072214, 2070620, 2069116, 2067669, 2066261, 2064881, 2063524, 2062187, 2060866, 2059560, 2058268, 2056990, 2055725, 2054473, 2053232, 2052003, 2050785, + 2099020, 2093054, 2087465, 2082412, 2078120, 2074727, 2072132, 2070078, 2068330, 2066748, 2065257, 2063822, 2062424, 2061053, 2059704, 2058373, 2057058, 2055758, 2054472, 2053199, 2051938, 2050689, 2049452, 2048227, 2047012, + 2096139, 2090050, 2084324, 2079090, 2074583, 2071007, 2068315, 2066233, 2064490, 2062921, 2061444, 2060021, 2058634, 2057272, 2055931, 2054607, 2053298, 2052004, 2050723, 2049455, 2048198, 2046954, 2045721, 2044499, 2043288, + 2093358, 2087136, 2081273, 2075869, 2071144, 2067362, 2064552, 2062437, 2060699, 2059144, 2057681, 2056271, 2054895, 2053542, 2052209, 2050892, 2049590, 2048301, 2047025, 2045762, 2044510, 2043270, 2042041, 2040823, 2039615, + 2090695, 2084324, 2078319, 2072754, 2067815, 2063804, 2060851, 2058694, 2056960, 2055421, 2053974, 2052577, 2051211, 2049868, 2048543, 2047233, 2045937, 2044654, 2043384, 2042125, 2040878, 2039642, 2038416, 2037202, 2035998, + 2088176, 2081633, 2075473, 2069749, 2064606, 2060351, 2057224, 2055012, 2053282, 2051760, 2050329, 2048945, 2047590, 2046256, 2044939, 2043636, 2042347, 2041069, 2039804, 2038550, 2037307, 2036075, 2034854, 2033643, 2032443, + 2085829, 2079090, 2072754, 2066864, 2061528, 2057022, 2053685, 2051401, 2049673, 2048168, 2046754, 2045384, 2044040, 2042715, 2041405, 2040109, 2038826, 2037554, 2036293, 2035044, 2033806, 2032578, 2031361, 2030154, 2028957, + 2083679, 2076729, 2070188, 2064115, 2058591, 2053838, 2050258, 2047874, 2046146, 2044660, 2043262, 2041905, 2040571, 2039255, 2037953, 2036664, 2035386, 2034120, 2032864, 2031619, 2030385, 2029162, 2027948, 2026745, 2025552, + 2081743, 2074583, 2067815, 2061528, 2055808, 2050823, 2046971, 2044453, 2042718, 2041250, 2039868, 2038524, 2037201, 2035893, 2034598, 2033315, 2032043, 2030782, 2029532, 2028291, 2027062, 2025842, 2024632, 2023433, 2022243, + 2080024, 2072674, 2065676, 2059146, 2053205, 2047996, 2043863, 2041167, 2039414, 2037963, 2036597, 2035265, 2033952, 2032652, 2031364, 2030087, 2028821, 2027565, 2026319, 2025083, 2023857, 2022642, 2021436, 2020240, 2019053, + 2078510, 2071007, 2063804, 2057022, 2050823, 2045382, 2040980, 2038059, 2036270, 2034834, 2033484, 2032163, 2030858, 2029566, 2028284, 2027013, 2025752, 2024501, 2023260, 2022028, 2020806, 2019594, 2018392, 2017199, 2016016, + 2077178, 2069565, 2062204, 2055199, 2048725, 2043022, 2038367, 2035193, 2033339, 2031915, 2030578, 2029267, 2027970, 2026685, 2025409, 2024143, 2022887, 2021641, 2020403, 2019176, 2017958, 2016749, 2015550, 2014361, 2013181, + 2075997, 2068315, 2060851, 2053685, 2046971, 2040980, 2036071, 2032650, 2030696, 2029277, 2027951, 2026649, 2025359, 2024080, 2022810, 2021549, 2020297, 2019054, 2017821, 2016597, 2015382, 2014177, 2012981, 2011794, 2010617, + 2074935, 2067217, 2059698, 2052442, 2045568, 2039324, 2034148, 2030513, 2028437, 2027013, 2025696, 2024401, 2023117, 2021842, 2020576, 2019319, 2018071, 2016832, 2015602, 2014381, 2013169, 2011966, 2010772, 2009588, 2008413, + 2073965, 2066233, 2058694, 2051401, 2044453, 2038059, 2032650, 2028831, 2026648, 2025207, 2023895, 2022606, 2021326, 2020055, 2018792, 2017538, 2016293, 2015056, 2013828, 2012609, 2011399, 2010198, 2009007, 2007824, 2006651, + 2073064, 2065332, 2057793, 2050495, 2043530, 2037084, 2031545, 2027580, 2025330, 2023867, 2022558, 2021272, 2019995, 2018726, 2017465, 2016213, 2014969, 2013733, 2012507, 2011289, 2010081, 2008881, 2007691, 2006509, 2005337, + 2072214, 2064490, 2056960, 2049673, 2042718, 2036270, 2030696, 2026648, 2024360, 2022884, 2021574, 2020289, 2019013, 2017745, 2016486, 2015234, 2013991, 2012757, 2011531, 2010314, 2009106, 2007907, 2006717, 2005536, 2004364, + 2071402, 2063691, 2056175, 2048905, 2041967, 2035534, 2029963, 2025892, 2023579, 2022099, 2020786, 2019502, 2018227, 2016960, 2015701, 2014450, 2013207, 2011973, 2010747, 2009531, 2008323, 2007124, 2005934, 2004754, 2003582, + 2070620, 2062921, 2055421, 2048168, 2041250, 2034834, 2029277, 2025207, 2022884, 2021401, 2020087, 2018803, 2017528, 2016261, 2015002, 2013751, 2012509, 2011275, 2010049, 2008833, 2007625, 2006427, 2005237, 2004056, 2002885, + 2069859, 2062174, 2054690, 2047454, 2040553, 2034154, 2028609, 2024546, 2022222, 2020737, 2019423, 2018139, 2016864, 2015597, 2014338, 2013087, 2011845, 2010611, 2009386, 2008169, 2006962, 2005763, 2004573, 2003393, 2002221, + 2069116, 2061444, 2053974, 2046754, 2039868, 2033484, 2027951, 2023895, 2021574, 2020087, 2018773, 2017489, 2016214, 2014947, 2013688, 2012437, 2011195, 2009961, 2008735, 2007519, 2006311, 2005113, 2003923, 2002743, 2001571, + 2068387, 2060727, 2053271, 2046065, 2039193, 2032821, 2027298, 2023249, 2020930, 2019444, 2018129, 2016845, 2015570, 2014303, 2013044, 2011793, 2010551, 2009317, 2008091, 2006875, 2005667, 2004469, 2003279, 2002099, 2000927, + 2067669, 2060021, 2052577, 2045384, 2038524, 2032163, 2026649, 2022606, 2020289, 2018803, 2017489, 2016204, 2014929, 2013662, 2012403, 2011152, 2009910, 2008676, 2007451, 2006234, 2005027, 2003828, 2002638, 2001458, 2000286, + 2066961, 2059324, 2051891, 2044709, 2037860, 2031509, 2026003, 2021965, 2019650, 2018165, 2016850, 2015565, 2014291, 2013024, 2011765, 2010514, 2009271, 2008037, 2006812, 2005596, 2004388, 2003190, 2002000, 2000819, 1999648, + 2066261, 2058634, 2051211, 2044040, 2037201, 2030858, 2025359, 2021326, 2019013, 2017528, 2016214, 2014929, 2013654, 2012387, 2011128, 2009878, 2008635, 2007401, 2006176, 2004959, 2003752, 2002553, 2001364, 2000183, 1999011 +]; +/// The tail node net1 of the comparator (V) at 0.1 ns steps from 97.8 ns, gates held at 0.742 V (sar_kick_edge.cir). +pub const WAVE_N1 : [67]f64 = [ + 0.6542256, 0.6543501, 0.6545256, 0.6547666, 0.6550893, 0.6555114, 0.6560521, 0.6567317, + 0.6575691, 0.6585730, 0.6597286, 0.6609966, 0.6623347, 0.6637109, 0.6651017, 0.6664869, + 0.6678438, 0.6691423, 0.6703385, 0.6713651, 0.6721181, 0.6724365, 0.6720731, 0.6706550, + 0.6676368, 0.6622549, 0.6535001, 0.6401275, 0.6207174, 0.5938186, 0.5583555, 0.5153014, + 0.4724891, 0.4421034, 0.4242183, 0.4117453, 0.4007515, 0.3900918, 0.3795454, 0.3690954, + 0.3587632, 0.3485813, 0.3385869, 0.3288206, 0.3193251, 0.3101441, 0.3013200, 0.2928900, + 0.2848796, 0.2772970, 0.2701240, 0.2633038, 0.2567159, 0.2501450, 0.2433286, 0.2362094, + 0.2290232, 0.2219670, 0.2150625, 0.2082315, 0.2013549, 0.1942302, 0.1851496, 0.1523603, + 0.0858989, 0.0538328, 0.0395118 +]; +/// The drain ap of the input pair (V), the same instants; an is equal to it to 1 uV (the plates are alike). +pub const WAVE_AP : [67]f64 = [ + 1.5000447, 1.5000936, 1.5001601, 1.5002461, 1.5003536, 1.5004851, 1.5006430, 1.5008292, + 1.5010425, 1.5012712, 1.5014861, 1.5016588, 1.5017858, 1.5018805, 1.5019571, 1.5020254, + 1.5020917, 1.5021598, 1.5022323, 1.5023107, 1.5023958, 1.5024875, 1.5025850, 1.5026865, + 1.5027894, 1.5028905, 1.5029866, 1.5030749, 1.5031527, 1.5032132, 1.5032283, 1.5030553, + 1.5021370, 1.4997298, 1.4963764, 1.4929604, 1.4894481, 1.4855157, 1.4809413, 1.4755930, + 1.4693692, 1.4621719, 1.4538971, 1.4444313, 1.4336468, 1.4213956, 1.4074977, 1.3917194, + 1.3737356, 1.3530617, 1.3289389, 1.3001421, 1.2647454, 1.2203697, 1.1663119, 1.1043593, + 1.0345034, 0.9551962, 0.8656820, 0.7664015, 0.6590177, 0.5443240, 0.4077422, 0.2378032, + 0.1166858, 0.0739376, 0.0542738 +]; +/// The charge the gate of VDP has taken from its source since 97.8 ns, fC, the integral of i(VMP) that ngspice made in the deck (one pair, 36 devices). +pub const WAVE_QG_FC : [67]f64 = [ + 0.0000, -0.0034, -0.0083, -0.0151, -0.0240, -0.0356, -0.0504, -0.0688, + -0.0911, -0.1175, -0.1469, -0.1779, -0.2096, -0.2414, -0.2732, -0.3047, + -0.3356, -0.3652, -0.3926, -0.4164, -0.4344, -0.4430, -0.4367, -0.4074, + -0.3430, -0.2267, -0.0360, 0.2566, 0.6833, 1.2785, 2.0744, 3.0877, + 4.2596, 5.3557, 6.2003, 6.9018, 7.5962, 8.3396, 9.1453, 10.0130, + 10.9385, 11.9160, 12.9390, 14.0009, 15.0952, 16.2161, 17.3587, 18.5202, + 19.7012, 20.9080, 22.1559, 23.4748, 24.9173, 26.5577, 28.4433, 30.5478, + 32.8503, 35.3679, 38.1225, 41.1248, 44.3715, 47.9295, 52.9403, 66.4617, + 84.1408, 91.6179, 95.0497 +]; +/// The comparator's aperture (sar_kick_aperture.cir): a step of 1 mV on the gate of VDP at the time tau, the latch's two outputs dp and dn read 4.8 ns after the 0.1 V crossing of clkc. +/// Row 0 is the step at 98.3069 ns (before the edge): dd, de are v(dp), v(dn) in V. Rows 1 .. 16 are the steps at 102.3069 ns + 0.125 ns j, that is 3.0 ns + 0.125 ns j after the crossing at 99.3069 ns. +pub const AP_DD0 : f64 = 7.669417e-01; +pub const AP_DE0 : f64 = 7.306061e-01; +pub const AP_DD : [16]f64 = [ + 7.669506e-01, 7.669190e-01, 7.668529e-01, 7.666138e-01, + 7.662237e-01, 7.651748e-01, 7.639326e-01, 7.615909e-01, + 7.595718e-01, 7.565009e-01, 7.542084e-01, 7.512322e-01, + 7.495414e-01, 7.482350e-01, 7.480924e-01, 7.480928e-01 +]; +pub const AP_DE : [16]f64 = [ + 7.306327e-01, 7.306648e-01, 7.307315e-01, 7.309716e-01, + 7.313606e-01, 7.323950e-01, 7.336030e-01, 7.358456e-01, + 7.377517e-01, 7.406112e-01, 7.427143e-01, 7.453964e-01, + 7.468786e-01, 7.479620e-01, 7.481112e-01, 7.480951e-01 +]; +/// The same experiment at the step dv = 0, 0.5 mV and 2 mV at 98.3069 ns (the linearity rows). +pub const LIN_DD : [3]f64 = [7.480928e-01, 7.575226e-01, 7.857390e-01]; +pub const LIN_DE : [3]f64 = [7.480951e-01, 7.393803e-01, 7.129134e-01]; +/// sar_kick_late.cir, the golden sine run (sar_amp 0.6, 1.1 us) at the clock edge of six decisions: the clkc rises 1, 10, 11 (decisions 0, 9, 10 of the first conversion) and 12, 21, 22 (of the second). +/// Each window is 67 samples 0.1 ns apart starting at j0 * 50 ps; tcb and tcc are the times clkc crosses 0.1 V and 0.75 V, D the plates' VDP - VDN at the 0.1 V crossing. +pub const LATE_NW : usize = 6; +pub const LATE_RISE : [6]u32 = [1, 10, 11, 12, 21, 22]; +pub const LATE_TS_NS : [6]f64 = [97.75, 403.90, 437.60, 597.75, 911.30, 946.45]; +pub const LATE_TCB_NS : [6]f64 = [99.29136, 405.40330, 439.14370, 599.27810, 912.81830, 947.95620]; +pub const LATE_TCC_NS : [6]f64 = [103.09850, 409.22510, 442.96570, 603.03620, 916.64100, 951.77890]; +pub const LATE_D_V : [6]f64 = [-0.0314674, -0.0054492, -0.0068895, -0.2689156, -0.0068574, -0.0054187]; +/// The plates' own voltages at the 0.1 V crossing: VDP, then VDN (V). +pub const LATE_VP_V : [6]f64 = [0.7261977, 0.7385431, 0.7378228, 0.6151386, 0.7452450, 0.7459631]; +pub const LATE_VN_V : [6]f64 = [0.7576651, 0.7439923, 0.7447123, 0.8840542, 0.7521024, 0.7513818]; +/// The tail node net1 of the comparator (V), window w at 67 w + k. +pub const LATE_N1 : [402]f64 = [ + 0.6605089, 0.6606132, 0.6607653, 0.6609799, 0.6612726, 0.6616611, 0.6621644, 0.6628028, + 0.6635964, 0.6645585, 0.6656818, 0.6669311, 0.6682611, 0.6696360, 0.6710306, 0.6724242, + 0.6737955, 0.6751170, 0.6763493, 0.6774323, 0.6782734, 0.6787292, 0.6785782, 0.6774847, + 0.6749519, 0.6702742, 0.6625016, 0.6504369, 0.6326805, 0.6077370, 0.5742609, 0.5319250, + 0.4848174, 0.4452965, 0.4211392, 0.4066056, 0.3951459, 0.3841904, 0.3731825, 0.3621109, + 0.3510520, 0.3400834, 0.3292774, 0.3187031, 0.3084272, 0.2985139, 0.2890209, 0.2799926, + 0.2714505, 0.2633849, 0.2557456, 0.2484294, 0.2412641, 0.2340201, 0.2265098, 0.2186784, + 0.2106548, 0.2026677, 0.1948269, 0.1871352, 0.1795837, 0.1721581, 0.1643652, 0.1536909, + 0.1226646, 0.0805913, 0.0751985, 0.6142614, 0.6145534, 0.6148958, 0.6153024, 0.6157886, + 0.6163712, 0.6170684, 0.6178995, 0.6188823, 0.6200247, 0.6213114, 0.6227032, 0.6241584, + 0.6256456, 0.6271419, 0.6286276, 0.6300807, 0.6314719, 0.6327585, 0.6338749, 0.6347199, + 0.6351367, 0.6348848, 0.6336021, 0.6307598, 0.6256190, 0.6172074, 0.6043362, 0.5856872, + 0.5600464, 0.5270367, 0.4894351, 0.4557894, 0.4329202, 0.4180914, 0.4063561, 0.3953556, + 0.3844564, 0.3735751, 0.3627410, 0.3520000, 0.3414014, 0.3309953, 0.3208330, 0.3109662, + 0.3014463, 0.2923204, 0.2836252, 0.2753795, 0.2675764, 0.2601746, 0.2530844, 0.2461438, + 0.2391031, 0.2317307, 0.2240600, 0.2163296, 0.2087103, 0.2012357, 0.1938717, 0.1865737, + 0.1792752, 0.1710400, 0.1548780, 0.1025773, 0.0621344, 0.0474224, 0.6142150, 0.6144901, + 0.6148103, 0.6151890, 0.6156408, 0.6161820, 0.6168302, 0.6176046, 0.6185240, 0.6196022, + 0.6208349, 0.6221906, 0.6236254, 0.6251032, 0.6265987, 0.6280915, 0.6295611, 0.6309822, + 0.6323184, 0.6335148, 0.6344870, 0.6351038, 0.6351632, 0.6343582, 0.6322338, 0.6281409, + 0.6212024, 0.6103100, 0.5941785, 0.5714988, 0.5413765, 0.5048105, 0.4680888, 0.4406783, + 0.4233916, 0.4109077, 0.3997682, 0.3888566, 0.3779665, 0.3671068, 0.3563209, 0.3456571, + 0.3351655, 0.3248968, 0.3149028, 0.3052353, 0.2959438, 0.2870702, 0.2786416, 0.2706625, + 0.2631069, 0.2559072, 0.2489343, 0.2419723, 0.2347515, 0.2271744, 0.2194343, 0.2117566, + 0.2042247, 0.1968219, 0.1895021, 0.1822310, 0.1746660, 0.1641600, 0.1282515, 0.0721885, + 0.0503147, 0.7664703, 0.7665753, 0.7667302, 0.7669496, 0.7672495, 0.7676479, 0.7681638, + 0.7688179, 0.7696297, 0.7706107, 0.7717497, 0.7730083, 0.7743413, 0.7757136, 0.7770999, + 0.7784785, 0.7798253, 0.7811085, 0.7822818, 0.7832747, 0.7839781, 0.7842237, 0.7837530, + 0.7821761, 0.7789217, 0.7731883, 0.7639152, 0.7497939, 0.7293337, 0.7010031, 0.6635909, + 0.6176249, 0.5696430, 0.5315189, 0.5063649, 0.4886891, 0.4736634, 0.4591912, 0.4447393, + 0.4303094, 0.4159996, 0.4018921, 0.3880477, 0.3745156, 0.3613358, 0.3485291, 0.3360724, + 0.3238566, 0.3116546, 0.2992173, 0.2864587, 0.2735362, 0.2606975, 0.2480459, 0.2354484, + 0.2220116, 0.2030806, 0.1768715, 0.1606448, 0.1607276, 0.1567777, 0.1423835, 0.1244187, + 0.1067105, 0.0916815, 0.0797361, 0.0700088, 0.6220232, 0.6223001, 0.6226256, 0.6230132, + 0.6234783, 0.6240373, 0.6247084, 0.6255109, 0.6264629, 0.6275747, 0.6288351, 0.6302076, + 0.6316493, 0.6331272, 0.6346175, 0.6361002, 0.6375535, 0.6389497, 0.6402478, 0.6413860, + 0.6422685, 0.6427469, 0.6425936, 0.6414643, 0.6388541, 0.6340529, 0.6261172, 0.6138814, + 0.5960317, 0.5713104, 0.5391391, 0.5016447, 0.4666560, 0.4420976, 0.4264161, 0.4144260, + 0.4033693, 0.3924518, 0.3815517, 0.3706920, 0.3599170, 0.3492742, 0.3388122, 0.3285796, + 0.3186254, 0.3089987, 0.2997455, 0.2909040, 0.2824972, 0.2745242, 0.2669508, 0.2596949, + 0.2526018, 0.2454252, 0.2379187, 0.2300831, 0.2221532, 0.2143216, 0.2066361, 0.1990631, + 0.1915496, 0.1840152, 0.1755399, 0.1584619, 0.1054996, 0.0624157, 0.0459403, 0.6216258, + 0.6219106, 0.6222455, 0.6226443, 0.6231224, 0.6236965, 0.6243848, 0.6252068, 0.6261801, + 0.6273131, 0.6285913, 0.6299761, 0.6314255, 0.6329077, 0.6343996, 0.6358814, 0.6373309, + 0.6387189, 0.6400028, 0.6411172, 0.6419611, 0.6423784, 0.6421294, 0.6408531, 0.6380221, + 0.6328992, 0.6245133, 0.6116758, 0.5930664, 0.5674650, 0.5344791, 0.4968285, 0.4629420, + 0.4397942, 0.4248313, 0.4130522, 0.4020361, 0.3911261, 0.3802325, 0.3693831, 0.3586233, + 0.3480010, 0.3375649, 0.3273641, 0.3174475, 0.3078642, 0.2986596, 0.2898706, 0.2815175, + 0.2735965, 0.2660685, 0.2588444, 0.2517591, 0.2445567, 0.2370052, 0.2291439, 0.2212186, + 0.2134049, 0.2057361, 0.1981731, 0.1906629, 0.1830994, 0.1743065, 0.1543187, 0.0984489, + 0.0604174, 0.0464518 +]; +/// The drain ap of the input pair (V), the same instants. +pub const LATE_AP : [402]f64 = [ + 1.5000299, 1.5000734, 1.5001338, 1.5002130, 1.5003130, 1.5004362, 1.5005850, 1.5007616, + 1.5009665, 1.5011926, 1.5014163, 1.5016053, 1.5017467, 1.5018506, 1.5019319, 1.5020020, + 1.5020683, 1.5021352, 1.5022056, 1.5022813, 1.5023633, 1.5024515, 1.5025451, 1.5026424, + 1.5027404, 1.5028355, 1.5029237, 1.5030014, 1.5030652, 1.5031100, 1.5031166, 1.5029921, + 1.5023090, 1.5000735, 1.4961675, 1.4918372, 1.4875689, 1.4830693, 1.4780126, 1.4722008, + 1.4655033, 1.4578080, 1.4490012, 1.4389611, 1.4275531, 1.4146227, 1.3999827, 1.3833893, + 1.3644984, 1.3427940, 1.3174703, 1.2872670, 1.2503904, 1.2052513, 1.1524033, 1.0931826, + 1.0266331, 0.9513855, 0.8671769, 0.7749111, 0.6767481, 0.5761650, 0.4735214, 0.3714587, + 0.3270070, 0.3060934, 0.2466242, 1.5000395, 1.5000902, 1.5001577, 1.5002439, 1.5003509, + 1.5004811, 1.5006368, 1.5008201, 1.5010299, 1.5012554, 1.5014688, 1.5016419, 1.5017700, + 1.5018657, 1.5019430, 1.5020117, 1.5020779, 1.5021458, 1.5022177, 1.5022952, 1.5023791, + 1.5024690, 1.5025640, 1.5026619, 1.5027599, 1.5028541, 1.5029404, 1.5030145, 1.5030699, + 1.5030890, 1.5030058, 1.5025751, 1.5012374, 1.4987528, 1.4957189, 1.4925348, 1.4890873, + 1.4851552, 1.4805787, 1.4752461, 1.4690612, 1.4619286, 1.4537473, 1.4444083, 1.4337913, + 1.4217596, 1.4081491, 1.3927495, 1.3752688, 1.3552741, 1.3320929, 1.3046522, 1.2712722, + 1.2297662, 1.1790927, 1.1209039, 1.0558879, 0.9828274, 0.9009462, 0.8105582, 0.7130978, + 0.6106679, 0.4950973, 0.3341536, 0.1485224, 0.0700023, 0.0364768, 1.5000230, 1.5000674, + 1.5001278, 1.5002062, 1.5003045, 1.5004250, 1.5005701, 1.5007421, 1.5009418, 1.5011637, + 1.5013868, 1.5015786, 1.5017237, 1.5018304, 1.5019135, 1.5019846, 1.5020512, 1.5021179, + 1.5021880, 1.5022631, 1.5023444, 1.5024319, 1.5025251, 1.5026222, 1.5027206, 1.5028169, + 1.5029069, 1.5029865, 1.5030506, 1.5030880, 1.5030613, 1.5028238, 1.5019224, 1.4998221, + 1.4968706, 1.4937024, 1.4903589, 1.4866146, 1.4822792, 1.4772257, 1.4713540, 1.4645686, + 1.4567704, 1.4478533, 1.4377010, 1.4261828, 1.4131457, 1.3983987, 1.3816846, 1.3626311, + 1.3406685, 1.3148955, 1.2838787, 1.2455432, 1.1980939, 1.1424128, 1.0800266, 1.0100256, + 0.9312904, 0.8437491, 0.7484265, 0.6475774, 0.5402376, 0.4052145, 0.2209993, 0.1073559, + 0.0730769, 1.5000335, 1.5000791, 1.5001419, 1.5002237, 1.5003266, 1.5004530, 1.5006054, + 1.5007858, 1.5009941, 1.5012217, 1.5014426, 1.5016256, 1.5017614, 1.5018616, 1.5019410, + 1.5020103, 1.5020764, 1.5021436, 1.5022146, 1.5022910, 1.5023736, 1.5024623, 1.5025560, + 1.5026527, 1.5027493, 1.5028418, 1.5029261, 1.5029982, 1.5030536, 1.5030818, 1.5030374, + 1.5026921, 1.5011466, 1.4970452, 1.4907687, 1.4837498, 1.4763747, 1.4682493, 1.4588947, + 1.4479514, 1.4351387, 1.4201824, 1.4027630, 1.3824731, 1.3587607, 1.3308281, 1.2974590, + 1.2568239, 1.2068811, 1.1471172, 1.0771956, 0.9942540, 0.8958693, 0.7818424, 0.6542477, + 0.5185342, 0.3903719, 0.2972025, 0.2596659, 0.2647511, 0.2559721, 0.2227682, 0.1840809, + 0.1481562, 0.1197119, 0.0990988, 0.0840120, 1.5000331, 1.5000813, 1.5001461, 1.5002294, + 1.5003332, 1.5004598, 1.5006117, 1.5007909, 1.5009972, 1.5012217, 1.5014390, 1.5016191, + 1.5017533, 1.5018530, 1.5019324, 1.5020018, 1.5020682, 1.5021356, 1.5022069, 1.5022835, + 1.5023664, 1.5024555, 1.5025499, 1.5026476, 1.5027458, 1.5028408, 1.5029285, 1.5030048, + 1.5030636, 1.5030895, 1.5030267, 1.5026623, 1.5014618, 1.4990685, 1.4960141, 1.4927990, + 1.4893594, 1.4854649, 1.4809410, 1.4756687, 1.4695492, 1.4624855, 1.4543752, 1.4451071, + 1.4345579, 1.4225862, 1.4090233, 1.3936540, 1.3761839, 1.3561816, 1.3329813, 1.3055245, + 1.2721558, 1.2307168, 1.1801148, 1.1218417, 1.0565475, 0.9829868, 0.9003373, 0.8088842, + 0.7100265, 0.6059407, 0.4901311, 0.3361290, 0.1669191, 0.0937777, 0.0699290, 1.5000383, + 1.5000885, 1.5001555, 1.5002411, 1.5003476, 1.5004771, 1.5006322, 1.5008148, 1.5010239, + 1.5012491, 1.5014633, 1.5016377, 1.5017670, 1.5018635, 1.5019412, 1.5020100, 1.5020763, + 1.5021440, 1.5022159, 1.5022932, 1.5023769, 1.5024666, 1.5025615, 1.5026595, 1.5027575, + 1.5028518, 1.5029385, 1.5030131, 1.5030692, 1.5030894, 1.5030083, 1.5025834, 1.5012609, + 1.4987848, 1.4957415, 1.4925499, 1.4891034, 1.4851767, 1.4806061, 1.4752780, 1.4690956, + 1.4619623, 1.4537757, 1.4444245, 1.4337848, 1.4217142, 1.4080421, 1.3925496, 1.3749350, + 1.3547536, 1.3313162, 1.3035243, 1.2696637, 1.2275484, 1.1762387, 1.1173641, 1.0514503, + 0.9772280, 0.8939458, 0.8019616, 0.7027522, 0.5981737, 0.4789743, 0.3133794, 0.1386773, + 0.0691370, 0.0375843 +]; +/// The aperture at a late decision (sar_kick_late_aperture.cir): the same experiment as AP_DD / AP_DE with the plates as the sine run left them and a series source +/// feeding the step to the comparator's gate. Set 0 of the deck (sar_which 0): the first conversion's tenth decision (clkc rise 10); set 1: the second conversion's (rise 21). +/// LAP_TCB_NS: the 0.1 V crossing of clkc in the unperturbed run (the first LINEAR row), to compare with sar_kick_late.cir's. +pub const LAP_TCB_NS : [2]f64 = [405.4019, 912.8204]; +pub const LAP_DD0 : [2]f64 = [7.2014660e-01, 7.1148650e-01]; +pub const LAP_DE0 : [2]f64 = [6.5699640e-01, 6.8795340e-01]; +pub const LAP_DD : [32]f64 = [ + 7.1998760e-01, 7.1991140e-01, 7.1976860e-01, 7.1954040e-01, + 7.1910730e-01, 7.1845500e-01, 7.1739260e-01, 7.1609130e-01, + 7.1443300e-01, 7.1274070e-01, 7.1087300e-01, 7.0922130e-01, + 7.0779840e-01, 7.0696390e-01, 7.0672820e-01, 7.0672170e-01, + 7.1132300e-01, 7.1123890e-01, 7.1109950e-01, 7.1084440e-01, + 7.1041830e-01, 7.0968920e-01, 7.0864730e-01, 7.0720100e-01, + 7.0558110e-01, 7.0370740e-01, 7.0189180e-01, 7.0007740e-01, + 6.9871230e-01, 6.9782050e-01, 6.9761310e-01, 6.9760820e-01 +]; +pub const LAP_DE : [32]f64 = [ + 6.5720690e-01, 6.5728610e-01, 6.5743380e-01, 6.5766760e-01, + 6.5810260e-01, 6.5873890e-01, 6.5973270e-01, 6.6090230e-01, + 6.6233790e-01, 6.6375890e-01, 6.6527630e-01, 6.6657270e-01, + 6.6763840e-01, 6.6822610e-01, 6.6843080e-01, 6.6843550e-01, + 6.8816610e-01, 6.8825240e-01, 6.8839500e-01, 6.8865410e-01, + 6.8907980e-01, 6.8978990e-01, 6.9076660e-01, 6.9207070e-01, + 6.9347930e-01, 6.9506210e-01, 6.9654910e-01, 6.9798710e-01, + 6.9902010e-01, 6.9965360e-01, 6.9983160e-01, 6.9983440e-01 +]; +/// The step at the crossing minus 1 ns at dv = 0, 0.5 mV and 2 mV, per set (3 each). +pub const LAP_LIN_DD : [6]f64 = [7.0672170e-01, 7.1343050e-01, 7.3360010e-01, 6.9760820e-01, 7.0454280e-01, 7.2539910e-01]; +pub const LAP_LIN_DE : [6]f64 = [6.6843550e-01, 6.6273400e-01, 6.4541350e-01, 6.9983440e-01, 6.9391220e-01, 6.7592760e-01]; +/// A MOVED bit's two-port (sar_kick_cnode_moved.cir, 50 C): the same experiment as Y_I100 with the bit's clock high, each plate node held 10 mV off its rail. 8 numbers per direction +/// (port 1 excited: i(vpb) re, im, i(vnb) re, im; port 2 excited: the same), direction 0 (the P plate UP: Bit at 1.49 V, Bit_n at 0.01 V) first, then direction 1 (P DOWN). +pub const Y_MV100 : [16]f64 = [ + -5.30457440e-04, -3.14486590e-05, -7.91310540e-07, 1.32829770e-06, 2.41375135e-08, 1.30295279e-06, -3.48619930e-04, -7.08728680e-06, + -6.99152220e-04, -2.58662580e-05, -6.38725210e-07, 2.81007489e-06, 6.25914436e-08, 2.76852114e-06, -2.64380360e-04, -1.21558460e-05 +]; +pub const Y_MV300 : [16]f64 = [ + -5.35449490e-04, -9.37882200e-05, -7.48647980e-07, 3.98317003e-06, 5.89978956e-08, 3.90805149e-06, -3.48947890e-04, -2.12434220e-05, + -7.02686360e-04, -7.72080680e-05, -1.42347860e-07, 8.38691915e-06, 5.47796715e-07, 8.26342132e-06, -2.65642800e-04, -3.63370020e-05 +]; +/// The operating points' currents in VPB and VNB (A): direction 0 then direction 1; 10 mV over each is the on-resistance of the path. +pub const MV_OP_I : [4]f64 = [5.31595983e-06, -3.49424300e-06, -7.00450250e-06, 2.65103965e-06]; +/// ngspice's own gate capacitances of one device at Vg 0.742 V, Vs 0.4 V, Vd 1.3 V, aF (sar_kick_qg.cir, `op`): the channel (cgg), the source overlap and the drain overlap. +pub const OP_CGG_AF : f64 = 1427.0; +pub const OP_CGSOL_AF : f64 = 622.0; +pub const OP_CGDOL_AF : f64 = 569.0; +/// The two-port the drivers of an idle bit hang on Bit and Bit_n (sar_kick_cnode.cir, 50 C, both ports at 0.75 V): the currents ngspice printed in the sources VPB and VNB (A, volts are 1), +/// real and imaginary, as printed: port 1 excited (i(vpb) re, im, i(vnb) re, im), then port 2 excited; 100 MHz, then 300 MHz. The current into the network is minus the printed one. +pub const Y_I100 : [8]f64 = [-7.7334126e-04, -1.6094823e-05, 3.86570209e-04, -2.0072801e-06, 3.86570200e-04, -2.0072794e-06, -3.8667660e-04, -8.0481677e-06]; +pub const Y_I300 : [8]f64 = [-7.7409205e-04, -4.8192037e-05, 3.86147488e-04, -5.9923287e-06, 3.86147408e-04, -5.9923193e-06, -3.8709942e-04, -2.4114984e-05]; +; --- END PINNED DATA --------------------------------------------------------------------------------------------------------------------------- + +; --- derived constants ------------------------------------------------------------------------------------------------------------------ +/// pi. +pub const PI_F : f64 = 3.141592653589793; +/// A current-table entry in amperes: an entry is Id * 200 V/A * 2^-24 V units (M3b), so 1 / (200 * 2^24) A. +pub const I_UNIT_A : f64 = 0.000000000298023223876953125; +/// The latch's own offset: the decision is "above" when VDP - VDN + off > 0 (M4b-psp), V. +pub const LATCH_OFF_V : f64 = 0.000153; +/// The aperture table has 16 rows: the steps at 3.0 ns + 0.125 ns j after clkc crosses 0.1 V. +pub const AP_N : usize = 16; +pub const AP_TAU0 : f64 = 3.0; +pub const AP_DTAU : f64 = 0.125; + +pub fn clampf(x: f64, lo: f64, hi: f64) -> f64 { + if (x < lo) { return lo; } + if (x > hi) { return hi; } + return x; +} + +/// The wave v at the fractional sample kf, linear between samples. +pub fn at(v: []const f64, kf: f64) -> f64 { + const i : usize = kf as usize; + const f : f64 = kf - (i as f64); + return v[i] * (1.0 - f) + v[i + 1] * f; +} + +; --- the comparator's charge pulse, from the tables ------------------------------------------------------------------------------------ +/// The gate charge of the pair (36 devices) in fC at the source Vs and the drain Vd, from one of the three tables (M3a's bilinear reader on the 25 x 25 grid: the source axis has +/// cells of 31.25 mV, so it is read at 2 Vs, the drain axis at Vd); the table's origin is the gate at 0.742 V and the source and drain at ground. +pub fn qtab_fc(tab: []const u32, vs: f64, vd: f64) -> f64 { + const s : f64 = clampf(vs, 0.0, 0.75) * 2.0 * SCALE; + const d : f64 = clampf(vd, 0.0, 1.5) * SCALE; + return idf(tab, s, d) * QG_UNIT_FC * M_PAIR; +} + +/// The charge the pair's gates take from the plates since sample 0 at each sample, fC, from the gate-charge table `tab` read along the waves n1 and ap (67 samples each). +pub fn pulse_dq(tab: []const u32, n1: []const f64, ap: []const f64, dq: []f64) { + const q0 : f64 = qtab_fc(tab, n1[0], ap[0]); + var k : usize = 0; + while (k < NS) { + dq[k] = qtab_fc(tab, n1[k], ap[k]) - q0; + k = k + 1; + } +} + +/// The same pulse from M3c's table (TT_QG_N: bulk at the source, 27 C, axes Vgs and Vds), the negative control: its origin is Vgs = Vds = 0, the gate at 0.742 V. +pub fn pulse_dq_m3c(n1: []const f64, ap: []const f64, dq: []f64) { + const q0 : f64 = idf(&TT_QG_N, clampf(VG_PLATES - n1[0], 0.0, 1.5) * SCALE, clampf(ap[0] - n1[0], 0.0, 1.5) * SCALE) * QG_UNIT_FC * M_PAIR; + var k : usize = 0; + while (k < NS) { + const q : f64 = idf(&TT_QG_N, clampf(VG_PLATES - n1[k], 0.0, 1.5) * SCALE, clampf(ap[k] - n1[k], 0.0, 1.5) * SCALE) * QG_UNIT_FC * M_PAIR; + dq[k] = q - q0; + k = k + 1; + } +} + +/// The pair's gate capacitance in fF at sample k: dQ_G / dVg across the two tables at 0.717 and 0.767 V, 50 mV apart. +pub fn cgg_at(n1: []const f64, ap: []const f64, k: usize) -> f64 { + return (qtab_fc(&QG_HI, n1[k], ap[k]) - qtab_fc(&QG_LO, n1[k], ap[k])) / QG_DVG; +} + +/// The gate capacitance the network carries, fF: the average of cgg over the pulse, weighted by the charge each interval puts in (the intervals from the sample at or before the +/// 0.1 V crossing t01k to the one after the 0.75 V crossing t075k), because the matrix of M1's step is one constant matrix while the gate's capacitance falls as the source and +/// drain move. +pub fn cgg_pulse(dq: []const f64, n1: []const f64, ap: []const f64, t01k: f64, t075k: f64) -> f64 { + var num : f64 = 0.0; + var den : f64 = 0.0; + var i : usize = t01k as usize; + const i2 : usize = (t075k as usize) + 1; + while (i < i2) { + const w : f64 = dq[i + 1] - dq[i]; + num = num + w * (cgg_at(n1, ap, i) + cgg_at(n1, ap, i + 1)) / 2.0; + den = den + w; + i = i + 1; + } + return num / den; +} + +; --- the drivers, from the tables and the idle two-port ---------------------------------------------------------------------------------- +/// The on-resistance (ohm) of a device conducting at a rail with its gate at the other: the table's Id(Vgs = vg, Vds) at Vds = h and 2 h (h = 62.5 mV) gives the slope +/// at Vds = 0 by a quadratic through the origin, a = (4 I1 - I2) / (2 h): the chord of the first cell would read the triode curve 1.7 percent soft. +pub fn r_tangent(t: []const u32, vg: f64) -> f64 { + const h : f64 = 0.0625; + const i1 : f64 = idf(t, vg * SCALE, h * SCALE) * I_UNIT_A; + const i2 : f64 = idf(t, vg * SCALE, 2.0 * h * SCALE) * I_UNIT_A; + return 2.0 * h / (4.0 * i1 - i2); +} + +/// The inverter's nmos holding its output at ground (gate 1.5 V), 50 C: ohm. XM2 of the golden inverter, W 1.0 um. +pub fn r_inv_n() -> f64 { + return r_tangent(&TT50_N, 1.5); +} + +/// The inverter's pmos holding its output at the rail (gate 0 V), 50 C: ohm. XM1, W 3.0 um. +pub fn r_inv_p() -> f64 { + return r_tangent(&TT50_P, 1.5); +} + +/// The resistance of a moved plate's driver (ohm): the P half's inverter, UP through the pmos, DOWN through the nmos. The N half's plate is behind the inverter x12 and then the +/// tgate x15, which at a rail is one device of the same size again: twice that, see n_drive. +pub fn p_drive(mode: u8) -> f64 { + if (mode == DR_UP) { return r_inv_p(); } + return r_inv_n(); +} + +pub fn n_drive(mode: u8) -> f64 { + return 2.0 * p_drive(mode); +} + +/// A real part of the two-port, microsiemens: minus the printed current, volts being 1. +pub fn y_g(a: []const f64, idx: usize) -> f64 { + return (0.0 - a[idx]) * 1000000.0; +} + +/// An imaginary part of the two-port as a capacitance in fF: Im Y / (2 pi f). +pub fn y_c(a: []const f64, idx: usize, f_hz: f64) -> f64 { + return (0.0 - a[idx]) / (2.0 * PI_F * f_hz) * 1000000000000000.0; +} + +/// The idle bit's conductance matrix at its two plate nodes (Bit_k, Bit_k_n), microsiemens: [0] G11, [1] G12 (negative), [2] G22. +pub fn idle_g(i: usize) -> f64 { + if (i == 0) { return y_g(&Y_I100, 0); } + if (i == 1) { return (y_g(&Y_I100, 2) + y_g(&Y_I100, 4)) / 2.0; } + return y_g(&Y_I100, 6); +} + +/// The idle bit's capacitance matrix, fF: [0] C11, [1] C12, [2] C22, at 100 MHz. +pub fn idle_c(i: usize) -> f64 { + if (i == 0) { return y_c(&Y_I100, 1, 100000000.0); } + if (i == 1) { return (y_c(&Y_I100, 3, 100000000.0) + y_c(&Y_I100, 5, 100000000.0)) / 2.0; } + return y_c(&Y_I100, 7, 100000000.0); +} + +/// The moved bit's two-port at its two plate nodes: dir 0 (the P plate UP) or 1 (DOWN). Its conductances, microsiemens: [0] G11, [1] G12, [2] G22. +pub fn moved_g(dir: usize, i: usize) -> f64 { + const o : usize = 8 * dir; + if (i == 0) { return y_g(&Y_MV100, o); } + if (i == 1) { return (y_g(&Y_MV100, o + 2) + y_g(&Y_MV100, o + 4)) / 2.0; } + return y_g(&Y_MV100, o + 6); +} + +/// The moved bit's capacitance matrix, fF at 100 MHz: [0] C11 (Bit to ground), [1] C12 (a normal coupling capacitor shows as a negative entry), [2] C22 (Bit_n). +pub fn moved_c(dir: usize, i: usize) -> f64 { + const o : usize = 8 * dir; + if (i == 0) { return y_c(&Y_MV100, o + 1, 100000000.0); } + if (i == 1) { return (y_c(&Y_MV100, o + 3, 100000000.0) + y_c(&Y_MV100, o + 5, 100000000.0)) / 2.0; } + return y_c(&Y_MV100, o + 7, 100000000.0); +} + +/// The tgate x11 between Bit and Bit_n, ohm: 1 / -Re Y12. +pub fn r_x11() -> f64 { + return 1000000.0 / (0.0 - idle_g(1)); +} + +/// The tgate x2 that holds Bit at VCM, ohm: 1 / (Re Y11 + Re Y12). +pub fn r_x2() -> f64 { + return 1000000.0 / (idle_g(0) + idle_g(1)); +} + +; --- the network ------------------------------------------------------------------------------------------------------------------------ +/// Add the admittance y between nodes a and b (either may be GND) to the NMAX x NMAX matrix m. +pub fn stamp_y(m: []f64, a: usize, b: usize, y: f64) { + if (a != GND) { m[a * NMAX + a] = m[a * NMAX + a] + y; } + if (b != GND) { m[b * NMAX + b] = m[b * NMAX + b] + y; } + if (a != GND && b != GND) { + m[a * NMAX + b] = m[a * NMAX + b] - y; + m[b * NMAX + a] = m[b * NMAX + a] - y; + } +} + +/// A capacitor y from node a to a plate whose voltage is minus node b's (an ideal source 1.5 V - Vb): the current into a is y d(Va + Vb) / dt, and b feels nothing. +pub fn stamp_follow(m: []f64, a: usize, b: usize, y: f64) { + m[a * NMAX + a] = m[a * NMAX + a] + y; + m[a * NMAX + b] = m[a * NMAX + b] + y; +} + +/// The conductance (G) and capacitance (C) matrices of the plate network. Nodes: 0 VDP, 1 net1 of the P half, 2 VDN, 3 net1 of the N half, then Bit_k = 4 + 3 k, Bit_k_n = 5 + 3 k and the +/// P plate behind its series resistor = 6 + 3 k, k = 0 .. 9 (plate k has UNITS[k] units on the side SIDE[k]: 1 is the top plate, 0 is net1, behind the one-unit bridge). +/// kp, kn: the mode of each plate (DR_*). rs: the resistor between each idle P plate and its Bit node (0 for none). cgg: the gate capacitance on each top plate, fF. +/// para: stamp the capacitances of the idle bit's two-port. gs: a factor on its conductances (1 for the measured two-port). +pub fn build(kp: []const u8, kn: []const u8, rs: f64, cgg: f64, para: bool, gs: f64, g: []f64, c: []f64) { + var i : usize = 0; + while (i < NSQ) { + g[i] = 0.0; + c[i] = 0.0; + i = i + 1; + } + stamp_y(c, 0, 1, C_UNIT_FF); + stamp_y(c, 2, 3, C_UNIT_FF); + stamp_y(c, 0, GND, C_EXPLICIT_FF + cgg); + stamp_y(c, 2, GND, C_EXPLICIT_FF + cgg); + var k : usize = 0; + while (k < PLATES) { + const cu : f64 = (UNITS[k] as f64) * C_UNIT_FF; + var tp : usize = 1; + var tn : usize = 3; + if (SIDE[k] == 1) { + tp = 0; + tn = 2; + } + const bit : usize = 4 + 3 * k; + const bnl : usize = bit + 1; + const pr : usize = bit + 2; + const pm : bool = kp[k] == DR_UP || kp[k] == DR_DOWN; + const nm : bool = kn[k] == DR_UP || kn[k] == DR_DOWN; + // the drivers: a moved plate is held through its inverter, an idle one is the measured two-port (x2 to VCM, x11 across) + if (pm) { stamp_y(g, bit, GND, 1000000.0 / p_drive(kp[k])); } + if (nm) { stamp_y(g, bnl, GND, 1000000.0 / n_drive(kn[k])); } + if (!pm && !nm) { + stamp_y(g, bit, GND, gs * (idle_g(0) + idle_g(1))); + stamp_y(g, bit, bnl, gs * (0.0 - idle_g(1))); + stamp_y(g, bnl, GND, gs * (idle_g(2) + idle_g(1))); + if (para) { + c[bit * NMAX + bit] = c[bit * NMAX + bit] + idle_c(0); + c[bnl * NMAX + bnl] = c[bnl * NMAX + bnl] + idle_c(2); + c[bit * NMAX + bnl] = c[bit * NMAX + bnl] + idle_c(1); + c[bnl * NMAX + bit] = c[bnl * NMAX + bit] + idle_c(1); + } + } + if (pm && nm && para) { + // the moved bit's own capacitances (its conductances are the inverters' above) + var dir : usize = 1; + if (kp[k] == DR_UP) { dir = 0; } + c[bit * NMAX + bit] = c[bit * NMAX + bit] + moved_c(dir, 0); + c[bnl * NMAX + bnl] = c[bnl * NMAX + bnl] + moved_c(dir, 2); + c[bit * NMAX + bnl] = c[bit * NMAX + bnl] + moved_c(dir, 1); + c[bnl * NMAX + bit] = c[bnl * NMAX + bit] + moved_c(dir, 1); + } + if (pm && !nm) { stamp_y(g, bnl, bit, gs * (0.0 - idle_g(1))); } + if (!pm && nm) { stamp_y(g, bit, GND, gs * (idle_g(0) + idle_g(1))); } + // the P plate + if (kp[k] == DR_MIRROR) { + stamp_follow(c, tp, bnl, cu); + stamp_y(g, pr, GND, G_SHORT); + } else if (kp[k] == DR_IDEAL) { + stamp_y(c, tp, GND, cu); + stamp_y(g, pr, GND, G_SHORT); + } else { + stamp_y(c, tp, pr, cu); + if (kp[k] == DR_IDLE && rs > 0.0) { + stamp_y(g, pr, bit, 1000000.0 / rs); + } else { + stamp_y(g, pr, bit, G_SHORT); + } + } + // the N plate + if (kn[k] == DR_MIRROR) { + stamp_follow(c, tn, bit, cu); + } else if (kn[k] == DR_IDEAL) { + stamp_y(c, tn, GND, cu); + } else { + stamp_y(c, tn, bnl, cu); + } + k = k + 1; + } +} + +; --- the solve: Gauss-Jordan with partial pivoting, n up to 34 (M1's own derivation is capped at 5 unknowns) ----------------------------------------- +/// Fill the work matrix [A | C / dt | b] and reduce A to the identity: the rest of each row becomes A^-1 [C / dt | b], that is M and N of M1's x' = M x + N u. A = G + C / dt; the input +/// is the charge u (fC) that a step takes out of the two top plates: b = (e_VDP + e_VDN) / dt, or the plate VDP's alone (bsel 1) or VDN's alone (bsel 2). +pub fn solve_mn(n: usize, g: []const f64, c: []const f64, h: f64, aug: []f64, bsel: usize) { + var i : usize = 0; + var j : usize = 0; + while (i < n) { + j = 0; + while (j < n) { + aug[i * WA + j] = g[i * NMAX + j] + c[i * NMAX + j] / h; + aug[i * WA + n + j] = c[i * NMAX + j] / h; + j = j + 1; + } + aug[i * WA + 2 * n] = 0.0; + if ((i == 0 && bsel != 2) || (i == 2 && bsel != 1)) { aug[i * WA + 2 * n] = 1.0 / h; } + i = i + 1; + } + var col : usize = 0; + while (col < n) { + var p : usize = col; + var r : usize = col + 1; + while (r < n) { + if (fabs64(aug[r * WA + col]) > fabs64(aug[p * WA + col])) { p = r; } + r = r + 1; + } + var k : usize = 0; + if (p != col) { + k = 0; + while (k <= 2 * n) { + const t : f64 = aug[col * WA + k]; + aug[col * WA + k] = aug[p * WA + k]; + aug[p * WA + k] = t; + k = k + 1; + } + } + const piv : f64 = aug[col * WA + col]; + k = col; + while (k <= 2 * n) { + aug[col * WA + k] = aug[col * WA + k] / piv; + k = k + 1; + } + r = 0; + while (r < n) { + if (r != col) { + const f : f64 = aug[r * WA + col]; + k = col; + while (k <= 2 * n) { + aug[r * WA + k] = aug[r * WA + k] - f * aug[col * WA + k]; + k = k + 1; + } + } + r = r + 1; + } + col = col + 1; + } +} + +/// The largest |A X - R| over the n + 1 solved columns, relative to the largest entry of R in the column: a solve that did not solve shows here. +pub fn solve_residual(n: usize, g: []const f64, c: []const f64, h: f64, aug: []const f64) -> f64 { + var worst : f64 = 0.0; + var i : usize = 0; + var col : usize = 0; + var k : usize = 0; + while (col <= n) { + var scale : f64 = 0.0; + i = 0; + while (i < n) { + var rhs : f64 = 0.0; + if (col < n) { rhs = c[i * NMAX + col] / h; } + if (col == n && (i == 0 || i == 2)) { rhs = 1.0 / h; } + if (fabs64(rhs) > scale) { scale = fabs64(rhs); } + i = i + 1; + } + i = 0; + while (i < n) { + var rhs : f64 = 0.0; + if (col < n) { rhs = c[i * NMAX + col] / h; } + if (col == n && (i == 0 || i == 2)) { rhs = 1.0 / h; } + var acc : f64 = 0.0 - rhs; + k = 0; + while (k < n) { + acc = acc + (g[i * NMAX + k] + c[i * NMAX + k] / h) * aug[k * WA + n + col]; + k = k + 1; + } + if (scale > 0.0 && fabs64(acc) / scale > worst) { worst = fabs64(acc) / scale; } + i = i + 1; + } + col = col + 1; + } + return worst; +} + +/// M1's words: D = M - I and N at 2^-24, sign-magnitude, row-major n x n. +pub fn quantize(n: usize, aug: []const f64, d: []u32, nv: []u32) { + var i : usize = 0; + var j : usize = 0; + while (i < n) { + j = 0; + while (j < n) { + var m : f64 = aug[i * WA + n + j]; + if (i == j) { m = m - 1.0; } + d[i * n + j] = q_sm(m); + j = j + 1; + } + nv[i] = q_sm(aug[i * WA + 2 * n]); + i = i + 1; + } +} + +; --- the run --------------------------------------------------------------------------------------------------------------------------- +/// The two top plates' voltage deviations at each of the NS samples, by M1's step (`sub` steps per sample; each takes the sample's charge increment / sub into both top plates). +pub fn run_q(n: usize, sub: usize, d: []const u32, nv: []const u32, dq: []const f64, vp: []f64, vn: []f64) { + var x : [34]u32 = [0] ** 34; + var y : [34]u32 = [0] ** 34; + vp[0] = 0.0; + vn[0] = 0.0; + var k : usize = 1; + while (k < NS) { + const u : u32 = q_sm(0.0 - (dq[k] - dq[k - 1]) / (sub as f64)); + var s : usize = 0; + while (s < sub) { + step(n, d, nv, &x, u, &y); + var i : usize = 0; + while (i < n) { + x[i] = y[i]; + i = i + 1; + } + s = s + 1; + } + vp[k] = sm_f64(x[0]); + vn[k] = sm_f64(x[2]); + k = k + 1; + } +} + +/// The same run in f64 with the unrounded M and N: x' = M x + N u, the reference the words are judged by. +pub fn run_f(n: usize, sub: usize, aug: []const f64, dq: []const f64, vp: []f64, vn: []f64) { + var x : [34]f64 = [0.0] ** 34; + var y : [34]f64 = [0.0] ** 34; + vp[0] = 0.0; + vn[0] = 0.0; + var k : usize = 1; + while (k < NS) { + const u : f64 = 0.0 - (dq[k] - dq[k - 1]) / (sub as f64); + var s : usize = 0; + while (s < sub) { + var i : usize = 0; + while (i < n) { + var acc : f64 = aug[i * WA + 2 * n] * u; + var j : usize = 0; + while (j < n) { + acc = acc + aug[i * WA + n + j] * x[j]; + j = j + 1; + } + y[i] = acc; + i = i + 1; + } + i = 0; + while (i < n) { + x[i] = y[i]; + i = i + 1; + } + s = s + 1; + } + vp[k] = x[0]; + vn[k] = x[2]; + k = k + 1; + } +} + +; --- the aperture and the decision -------------------------------------------------------------------------------------------------------- +/// The share of the decision that the input still decides at row j of the aperture table `set`: the latch's response to a 1 mV step at 3.0 ns + 0.125 ns j after clkc crosses +/// 0.1 V over its response to a step before the edge, each less the unperturbed run's own small difference. Set 0 is the first decision with the gates on ideal sources +/// (sar_kick_aperture.cir); sets 1 and 2 are the late decisions of the sine run's first and second conversion (sar_kick_late_aperture.cir, sar_which 0 and 1). +pub fn ap_share(set: usize, j: usize) -> f64 { + if (set == 0) { + const base : f64 = LIN_DD[0] - LIN_DE[0]; + const g0 : f64 = (AP_DD0 - AP_DE0) - base; + return ((AP_DD[j] - AP_DE[j]) - base) / g0; + } + const s : usize = set - 1; + const base : f64 = LAP_LIN_DD[3 * s] - LAP_LIN_DE[3 * s]; + const g0 : f64 = (LAP_DD0[s] - LAP_DE0[s]) - base; + return ((LAP_DD[16 * s + j] - LAP_DE[16 * s + j]) - base) / g0; +} + +/// What the decision reads of the plates' kick: the change of VDP - VDN since clkc crossed 0.1 V, weighed by the decision's share still open: each interval of the aperture table +/// carries (S_j - S_{j+1}) of the decision and is read at its middle. +pub fn kd_eff(vp: []const f64, vn: []const f64, t01k: f64, set: usize) -> f64 { + const d0 : f64 = at(vp, t01k) - at(vn, t01k); + var t : f64 = 0.0; + var j : usize = 0; + while (j + 1 < AP_N) { + const kk : f64 = t01k + (AP_TAU0 + AP_DTAU * ((j as f64) + 0.5)) / SAMPLE_NS; + t = t + (ap_share(set, j) - ap_share(set, j + 1)) * ((at(vp, kk) - at(vn, kk)) - d0); + j = j + 1; + } + return t; +} + +pub struct KickOut { dvdp: f64, dvdn: f64, off: f64 } + +/// One run of the network: the modes of the plates, the series resistor, the gate capacitance, the options, the step count per sample, the charge pulse dq (fC at each sample) and +/// the sample positions of the two crossings of clkc and the aperture set. dvdp, dvdn: what the edge did to the two plates between clkc = 0.1 V and 0.75 V (V); off: the decision's offset, kd_eff plus +/// the latch's own (V). +pub fn net_run(kp: []const u8, kn: []const u8, rs: f64, cgg: f64, para: bool, gs: f64, sub: usize, dq: []const f64, t01k: f64, t075k: f64, set: usize) -> KickOut { + var g : [1156]f64 = [0.0] ** 1156; + var c : [1156]f64 = [0.0] ** 1156; + var aug : [2346]f64 = [0.0] ** 2346; + var d : [1156]u32 = [0] ** 1156; + var nv : [34]u32 = [0] ** 34; + var vp : [67]f64 = [0.0] ** 67; + var vn : [67]f64 = [0.0] ** 67; + build(kp, kn, rs, cgg, para, gs, &g, &c); + solve_mn(NMAX, &g, &c, SAMPLE_NS / (sub as f64), &aug, 0); + quantize(NMAX, &aug, &d, &nv); + run_q(NMAX, sub, &d, &nv, dq, &vp, &vn); + return KickOut { dvdp: at(&vp, t075k) - at(&vp, t01k), dvdn: at(&vn, t075k) - at(&vn, t01k), off: kd_eff(&vp, &vn, t01k, set) + LATCH_OFF_V }; +} + +pub fn fill_u8(a: []u8, m: u8) { + var k : usize = 0; + while (k < PLATES) { + a[k] = m; + k = k + 1; + } +} + +/// The pulse selectors of kick_with. +pub const PULSE_TABLE : u8 = 0; +pub const PULSE_NONE : u8 = 1; +pub const PULSE_M3C : u8 = 2; + +/// A run with every P plate in mode mp and every N plate in mode mn: the six first-decision rows, the series resistor and the controls are all this. +pub fn kick_with(mp: u8, mn: u8, rs: f64, para: bool, gs: f64, sub: usize, pulse: u8) -> KickOut { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + fill_u8(&kp, mp); + fill_u8(&kn, mn); + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dq); + const cgg : f64 = cgg_pulse(&dq, &WAVE_N1, &WAVE_AP, T01_K, T075_K); + if (pulse == PULSE_NONE) { fill_f64(&dq, 0.0); } + if (pulse == PULSE_M3C) { pulse_dq_m3c(&WAVE_N1, &WAVE_AP, &dq); } + return net_run(&kp, &kn, rs, cgg, para, gs, sub, &dq, T01_K, T075_K, 0); +} + +pub fn fill_f64(a: []f64, v: f64) { + var k : usize = 0; + while (k < NS) { + a[k] = v; + k = k + 1; + } +} + +/// The first-decision experiment v of sar_kick.t27 (V_BASE .. V_PPLATE) as the network runs it. The tracking switch (V_IDEALSW) is not in the network: its row is the base circuit's. +/// The comparator isolated by buffers (V_NOKICK) has no charge pulse. +pub fn kick_row(v: usize) -> KickOut { + if (v == V_PLATES) { return kick_with(DR_IDEAL, DR_IDEAL, 0.0, true, 1.0, SUB, PULSE_TABLE); } + if (v == V_NPLATE) { return kick_with(DR_IDLE, DR_MIRROR, 0.0, true, 1.0, SUB, PULSE_TABLE); } + if (v == V_PPLATE) { return kick_with(DR_MIRROR, DR_IDLE, 0.0, true, 1.0, SUB, PULSE_TABLE); } + if (v == V_NOKICK) { return kick_with(DR_IDLE, DR_IDLE, 0.0, true, 1.0, SUB, PULSE_NONE); } + return kick_with(DR_IDLE, DR_IDLE, 0.0, true, 1.0, SUB, PULSE_TABLE); +} + +/// The P plates behind the resistor rs (ohm): the first decision's run. +pub fn kick_series(rs: f64) -> KickOut { + return kick_with(DR_IDLE, DR_IDLE, rs, true, 1.0, SUB, PULSE_TABLE); +} + +/// The resistance (ohm) at which the P plates' series resistor brings the derived first-decision offset down to the latch's own: bisection on 1300 .. 3900 ohm, where the derived +/// offset is above it at the first and below it at the second. +pub fn series_cross_ohm() -> f64 { + var lo : f64 = 1300.0; + var hi : f64 = 3900.0; + var it : u32 = 0; + while (it < 14) { + const mid : f64 = (lo + hi) / 2.0; + if (kick_series(mid).off > LATCH_OFF_V) { lo = mid; } else { hi = mid; } + it = it + 1; + } + return (lo + hi) / 2.0; +} + +/// The tgate at VCM from M3b's tables, ohm: the nmos (Vgs = 0.75 V) and the pmos (Vsg = 0.75 V) in parallel, each at Vds -> 0. The tables are for the source and bulk together (nmos) and the +/// n-well on the rail (pmos); the golden tgate has its nmos source at 0.75 V over a grounded bulk, so this leaves the body effect out. +pub fn r_tgate_tables() -> f64 { + const gn : f64 = 1.0 / r_tangent(&TT50_N, 0.75); + const gp : f64 = 1.0 / r_tangent(&TT50_P, 0.75); + return 1.0 / (gn + gp); +} + +/// How far the words of M1's step are from the f64 run of the same method, V: the largest |difference| of either top plate over the 67 samples, for the first-decision base circuit. +pub fn quant_err_v() -> f64 { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dq); + const cgg : f64 = cgg_pulse(&dq, &WAVE_N1, &WAVE_AP, T01_K, T075_K); + var g : [1156]f64 = [0.0] ** 1156; + var c : [1156]f64 = [0.0] ** 1156; + var aug : [2346]f64 = [0.0] ** 2346; + var d : [1156]u32 = [0] ** 1156; + var nv : [34]u32 = [0] ** 34; + var vp : [67]f64 = [0.0] ** 67; + var vn : [67]f64 = [0.0] ** 67; + var fp : [67]f64 = [0.0] ** 67; + var fn_ : [67]f64 = [0.0] ** 67; + build(&kp, &kn, 0.0, cgg, true, 1.0, &g, &c); + solve_mn(NMAX, &g, &c, SAMPLE_NS / (SUB as f64), &aug, 0); + quantize(NMAX, &aug, &d, &nv); + run_q(NMAX, SUB, &d, &nv, &dq, &vp, &vn); + run_f(NMAX, SUB, &aug, &dq, &fp, &fn_); + var worst : f64 = 0.0; + var k : usize = 0; + while (k < NS) { + if (fabs64(vp[k] - fp[k]) > worst) { worst = fabs64(vp[k] - fp[k]); } + if (fabs64(vn[k] - fn_[k]) > worst) { worst = fabs64(vn[k] - fn_[k]); } + k = k + 1; + } + return worst; +} + +/// The residual of the solve for the first-decision base circuit (relative). +pub fn base_residual() -> f64 { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dq); + const cgg : f64 = cgg_pulse(&dq, &WAVE_N1, &WAVE_AP, T01_K, T075_K); + var g : [1156]f64 = [0.0] ** 1156; + var c : [1156]f64 = [0.0] ** 1156; + var aug : [2346]f64 = [0.0] ** 2346; + build(&kp, &kn, 0.0, cgg, true, 1.0, &g, &c); + solve_mn(NMAX, &g, &c, SAMPLE_NS / (SUB as f64), &aug, 0); + return solve_residual(NMAX, &g, &c, SAMPLE_NS / (SUB as f64), &aug); +} + +; --- the late decisions: plates that have moved ------------------------------------------------------------------------------------------ +/// The bit of decision i (0 = the first) of the golden sine's conversion cv (0 or 1): the latch decides "above" (bit 1, the P plate goes down) when D + off > 0, with D the pinned +/// VDP - VDN before the decision (sar_kick.t27, DEC_D) and the offset the first decision's at i = 0, the mean late one from 1 to 6 (every D there is more than 8 mV from it but the first conversion's +/// sixth) and each decision's own measured one from 7 on. +pub fn sine_bit(cv: usize, i: usize) -> u8 { + var off : f64 = off_late_v(); + if (i == 0) { off = first_off(V_BASE); } + if (i >= 7) { off = 0.0 - late_thr(i as u32).mid; } + if (DEC_D[11 * cv + i] + off > 0.0) { return 1; } + return 0; +} + +/// The waves of window w of sar_kick_late.cir (w < LATE_NW), or those of the first-decision edge deck (w = LATE_NW), copied into n1 and ap. +pub fn window_waves(w: usize, n1: []f64, ap: []f64) { + var k : usize = 0; + while (k < NS) { + if (w < LATE_NW) { + n1[k] = LATE_N1[w * NS + k]; + ap[k] = LATE_AP[w * NS + k]; + } else { + n1[k] = WAVE_N1[k]; + ap[k] = WAVE_AP[k]; + } + k = k + 1; + } +} + +/// The sample position of the 0.1 V crossing of clkc in window w (the edge deck's when w = LATE_NW). +pub fn window_t01k(w: usize) -> f64 { + if (w < LATE_NW) { return (LATE_TCB_NS[w] - LATE_TS_NS[w]) / SAMPLE_NS; } + return T01_K; +} + +/// The same for the 0.75 V crossing. +pub fn window_t075k(w: usize) -> f64 { + if (w < LATE_NW) { return (LATE_TCC_NS[w] - LATE_TS_NS[w]) / SAMPLE_NS; } + return T075_K; +} + +/// The aperture set of window w: the late decisions of the first conversion (windows 1, 2) are set 1, those of the second (4, 5) set 2, the rest the first decision's. +pub fn window_set(w: usize) -> usize { + if (w == 1 || w == 2) { return 1; } + if (w == 4 || w == 5) { return 2; } + return 0; +} + +/// One run of the network with the plates in the modes kp, kn, driven by the comparator's edge of window w: its charge pulse and its pulse-weighted gate capacitance from the tables. +pub fn run_window(kp: []const u8, kn: []const u8, w: usize) -> KickOut { + return run_window_opts(kp, kn, w, window_set(w), true); +} + +/// The same with the aperture set and the bits' capacitances chosen: the controls. +pub fn run_window_opts(kp: []const u8, kn: []const u8, w: usize, set: usize, para: bool) -> KickOut { + var n1 : [67]f64 = [0.0] ** 67; + var ap : [67]f64 = [0.0] ** 67; + window_waves(w, &n1, &ap); + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &n1, &ap, &dq); + const cgg : f64 = cgg_pulse(&dq, &n1, &ap, window_t01k(w), window_t075k(w)); + return net_run(kp, kn, 0.0, cgg, para, 1.0, SUB, &dq, window_t01k(w), window_t075k(w), set); +} + +/// The plates of the decision with m plates moved (m = 9 for the tenth decision, 10 for the eleventh) of the golden sine's conversion cv: decision i moved plate 9 - i, the P plate +/// down (nmos of DAC_control's inverter) for bit 1 and up for bit 0, the N plate the other way behind x12 and x15; the others are idle. +pub fn late_modes(cv: usize, m: usize, kp: []u8, kn: []u8) { + var i : usize = 0; + while (i < m) { + const k : usize = 9 - i; + if (sine_bit(cv, i) == 1) { + kp[k] = DR_DOWN; + kn[k] = DR_UP; + } else { + kp[k] = DR_UP; + kn[k] = DR_DOWN; + } + i = i + 1; + } +} + +/// The decision with m plates moved of conversion cv, with the comparator's edge of window w (LATE_NW: the first decision's edge deck). +pub fn late_kick(cv: usize, m: usize, w: usize) -> KickOut { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + late_modes(cv, m, &kp, &kn); + return run_window(&kp, &kn, w); +} + +/// The same with the aperture set and the bits' capacitances chosen. +pub fn late_kick_opts(cv: usize, m: usize, w: usize, set: usize, para: bool) -> KickOut { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + late_modes(cv, m, &kp, &kn); + return run_window_opts(&kp, &kn, w, set, para); +} + +/// The window of the decision with m plates moved of conversion cv in sar_kick_late.cir: clkc rises 10 and 11 (conversion 0), 21 and 22 (conversion 1) for m = 9 and 10; the first +/// decision (m = 0) is rise 1 and 12. +pub fn own_window(cv: usize, m: usize) -> usize { + if (m == 0) { return 3 * cv; } + if (m == 9) { return 3 * cv + 1; } + return 3 * cv + 2; +} + +/// The offset of the decision with m plates moved (9 or 10) of conversion cv, each with its own edge and its own aperture: what the network derives, V. +pub fn late_off(cv: usize, m: usize) -> f64 { + return late_kick(cv, m, own_window(cv, m)).off; +} + +/// The late offset the network derives: the mean of the four decisions with nine and ten plates moved of the golden sine's two conversions, V. +pub fn late_off_derived() -> f64 { + return (late_off(0, 9) + late_off(0, 10) + late_off(1, 9) + late_off(1, 10)) / 4.0; +} + +; --- the pair's two gates at their own levels (the extension the three gate-level tables allow) ------------------------------------------ +/// The gate charge of the pair (fC) with its gate at vg (V): the three tables (0.717, 0.742, 0.767 V) interpolated linearly in vg, clamped to their range. +pub fn qtab_g(vg: f64, vs: f64, vd: f64) -> f64 { + const qm : f64 = qtab_fc(&QG_MID, vs, vd); + const x : f64 = clampf((vg - VG_PLATES) / QG_DVG_STEP, 0.0 - 1.0, 1.0); + if (x >= 0.0) { return qm + x * (qtab_fc(&QG_HI, vs, vd) - qm); } + return qm + x * (qm - qtab_fc(&QG_LO, vs, vd)); +} + +/// The charge a gate at vg takes from its plate since sample 0, fC, along the waves n1 and ap. +pub fn pulse_dq_g(n1: []const f64, ap: []const f64, vg: f64, dq: []f64) { + const q0 : f64 = qtab_g(vg, n1[0], ap[0]); + var k : usize = 0; + while (k < NS) { + dq[k] = qtab_g(vg, n1[k], ap[k]) - q0; + k = k + 1; + } +} + +/// net_run with a pulse of its own for each top plate: dqp into VDP, dqn into VDN. The network is linear, so the two inputs are two runs of M1's step (one input each, bsel 1 and 2) +/// that add. +pub fn net_run2(kp: []const u8, kn: []const u8, rs: f64, cgg: f64, para: bool, sub: usize, dqp: []const f64, dqn: []const f64, t01k: f64, t075k: f64, set: usize) -> KickOut { + var g : [1156]f64 = [0.0] ** 1156; + var c : [1156]f64 = [0.0] ** 1156; + var aug : [2346]f64 = [0.0] ** 2346; + var d : [1156]u32 = [0] ** 1156; + var nv : [34]u32 = [0] ** 34; + var vpa : [67]f64 = [0.0] ** 67; + var vna : [67]f64 = [0.0] ** 67; + var vpb : [67]f64 = [0.0] ** 67; + var vnb : [67]f64 = [0.0] ** 67; + build(kp, kn, rs, cgg, para, 1.0, &g, &c); + solve_mn(NMAX, &g, &c, SAMPLE_NS / (sub as f64), &aug, 1); + quantize(NMAX, &aug, &d, &nv); + run_q(NMAX, sub, &d, &nv, dqp, &vpa, &vna); + solve_mn(NMAX, &g, &c, SAMPLE_NS / (sub as f64), &aug, 2); + quantize(NMAX, &aug, &d, &nv); + run_q(NMAX, sub, &d, &nv, dqn, &vpb, &vnb); + var k : usize = 0; + while (k < NS) { + vpa[k] = vpa[k] + vpb[k]; + vna[k] = vna[k] + vnb[k]; + k = k + 1; + } + return KickOut { dvdp: at(&vpa, t075k) - at(&vpa, t01k), dvdn: at(&vna, t075k) - at(&vna, t01k), off: kd_eff(&vpa, &vna, t01k, set) + LATCH_OFF_V }; +} + +/// A run of the first decision's edge (the edge deck's waves) with the plates in modes mp, mn (every plate alike), the series resistor rs, and the pair's gates at 0.742 V + d / 2 +/// (VDP) and 0.742 V - d / 2 (VDN): d is VDP - VDN before the edge. +pub fn kick_with_g(mp: u8, mn: u8, rs: f64, d: f64) -> KickOut { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + fill_u8(&kp, mp); + fill_u8(&kn, mn); + var dqm : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dqm); + const cgg : f64 = cgg_pulse(&dqm, &WAVE_N1, &WAVE_AP, T01_K, T075_K); + var dqp : [67]f64 = [0.0] ** 67; + var dqn : [67]f64 = [0.0] ** 67; + pulse_dq_g(&WAVE_N1, &WAVE_AP, VG_PLATES + d / 2.0, &dqp); + pulse_dq_g(&WAVE_N1, &WAVE_AP, VG_PLATES - d / 2.0, &dqn); + return net_run2(&kp, &kn, rs, cgg, true, SUB, &dqp, &dqn, T01_K, T075_K, 0); +} + +/// The modes and the resistor of the first-decision experiment v (V_BASE .. V_PPLATE) as kick_row runs them, at VDP - VDN = d. +pub fn kick_row_g(v: usize, d: f64) -> KickOut { + if (v == V_PLATES) { return kick_with_g(DR_IDEAL, DR_IDEAL, 0.0, d); } + if (v == V_NPLATE) { return kick_with_g(DR_IDLE, DR_MIRROR, 0.0, d); } + if (v == V_PPLATE) { return kick_with_g(DR_MIRROR, DR_IDLE, 0.0, d); } + return kick_with_g(DR_IDLE, DR_IDLE, 0.0, d); +} + +/// The offset of experiment v with the plates at the D the decision itself flips at: the fixed point of d = -off, from d = 0 in four steps (each moves it by a tenth of the last). +pub fn off_row_g(v: usize) -> f64 { + var d : f64 = 0.0; + var i : u32 = 0; + while (i < 4) { + d = 0.0 - kick_row_g(v, d).off; + i = i + 1; + } + return 0.0 - d; +} + +/// The decision with m plates moved of conversion cv, with the edge of window w and the gates at the plates' own voltages in that window (the sine run's, pinned). +pub fn late_kick_g(cv: usize, m: usize, w: usize) -> KickOut { + return late_kick_gv(cv, m, w, LATE_VP_V[w], LATE_VN_V[w]); +} + +/// The same with the gates at vgp (VDP's) and vgn (VDN's). +pub fn late_kick_gv(cv: usize, m: usize, w: usize, vgp: f64, vgn: f64) -> KickOut { + var kp : [10]u8 = [0] ** 10; + var kn : [10]u8 = [0] ** 10; + late_modes(cv, m, &kp, &kn); + var n1 : [67]f64 = [0.0] ** 67; + var ap : [67]f64 = [0.0] ** 67; + window_waves(w, &n1, &ap); + var dqm : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &n1, &ap, &dqm); + const cgg : f64 = cgg_pulse(&dqm, &n1, &ap, window_t01k(w), window_t075k(w)); + var dqp : [67]f64 = [0.0] ** 67; + var dqn : [67]f64 = [0.0] ** 67; + pulse_dq_g(&n1, &ap, vgp, &dqp); + pulse_dq_g(&n1, &ap, vgn, &dqn); + return net_run2(&kp, &kn, 0.0, cgg, true, SUB, &dqp, &dqn, window_t01k(w), window_t075k(w), window_set(w)); +} + +; --- the pins ----------------------------------------------------------------------------------------------------------------------------- +/// An order-sensitive fold (M1's) of the f64 numbers a[0 .. n), each as q_sm(a * scale), onto the seed s. +pub fn fold_f(a: []const f64, n: usize, scale: f64, s: u64) -> u64 { + var acc : u64 = s; + var k : usize = 0; + while (k < n) { + acc = fold(acc, q_sm(a[k] * scale)); + k = k + 1; + } + return acc; +} + +/// The three gate-charge tables. +pub fn fold_tables() -> u64 { + return fold_arr(&QG_HI, 625, fold_arr(&QG_MID, 625, fold_arr(&QG_LO, 625, 0))); +} + +/// The comparator's waves: net1, ap and the gate charge ngspice integrated. +pub fn fold_waves() -> u64 { + return fold_f(&WAVE_QG_FC, NS, 1.0, fold_f(&WAVE_AP, NS, 1.0, fold_f(&WAVE_N1, NS, 1.0, 0))); +} + +/// The aperture rows and the idle two-port currents (the currents in mA). +pub fn fold_rest() -> u64 { + var s : u64 = fold_f(&AP_DD, AP_N, 1.0, 0); + s = fold_f(&AP_DE, AP_N, 1.0, s); + s = fold_f(&LIN_DD, 3, 1.0, s); + s = fold_f(&LIN_DE, 3, 1.0, s); + s = fold_f(&Y_I100, 8, 1000.0, s); + return fold_f(&Y_I300, 8, 1000.0, s); +} + +/// The six windows of sar_kick_late.cir. +pub fn fold_late() -> u64 { + var s : u64 = fold_arr(&LATE_RISE, LATE_NW, 0); + s = fold_f(&LATE_TS_NS, LATE_NW, 0.001, s); + s = fold_f(&LATE_TCB_NS, LATE_NW, 0.001, s); + s = fold_f(&LATE_TCC_NS, LATE_NW, 0.001, s); + s = fold_f(&LATE_D_V, LATE_NW, 1.0, s); + s = fold_f(&LATE_VP_V, LATE_NW, 1.0, s); + s = fold_f(&LATE_VN_V, LATE_NW, 1.0, s); + s = fold_f(&LATE_N1, LATE_NW * NS, 1.0, s); + return fold_f(&LATE_AP, LATE_NW * NS, 1.0, s); +} + +/// The two late apertures of sar_kick_late_aperture.cir. +pub fn fold_late_aperture() -> u64 { + var s : u64 = fold_f(&LAP_TCB_NS, 2, 0.001, 0); + s = fold_f(&LAP_DD0, 2, 1.0, s); + s = fold_f(&LAP_DE0, 2, 1.0, s); + s = fold_f(&LAP_DD, 32, 1.0, s); + s = fold_f(&LAP_DE, 32, 1.0, s); + s = fold_f(&LAP_LIN_DD, 6, 1.0, s); + return fold_f(&LAP_LIN_DE, 6, 1.0, s); +} + +/// The moved bit's two-port of sar_kick_cnode_moved.cir (the currents in mA). +pub fn fold_moved() -> u64 { + var s : u64 = fold_f(&Y_MV100, 16, 1000.0, 0); + s = fold_f(&Y_MV300, 16, 1000.0, s); + return fold_f(&MV_OP_I, 4, 1000.0, s); +} + +/// The sample index at or before the fractional position x. +pub fn sample_floor(x: f64) -> usize { + return x as usize; +} + +/// The units on one side of the bridge (1: the top plate, 0: net1). +pub fn units_on(side: u8) -> f64 { + var s : f64 = 0.0; + var k : usize = 0; + while (k < PLATES) { + if (SIDE[k] == side) { s = s + (UNITS[k] as f64); } + k = k + 1; + } + return s; +} + +/// The comparator's gate capacitance at the fractional sample kf, fF. +pub fn cgg_frac(kf: f64) -> f64 { + const i : usize = kf as usize; + const f : f64 = kf - (i as f64); + return cgg_at(&WAVE_N1, &WAVE_AP, i) * (1.0 - f) + cgg_at(&WAVE_N1, &WAVE_AP, i + 1) * f; +} + +/// The charge between the 0.1 V and the 0.75 V crossings of clkc, fC, from the wave dq. +pub fn dq_kick(dq: []const f64) -> f64 { + return at(dq, T075_K) - at(dq, T01_K); +} + +; --- the tests ------------------------------------------------------------------------------------------------------------------------------ + +test the_pinned_data_is_what_the_decks_printed { + assert_eq(fold_tables(), 1294308024549390123); + assert_eq(fold_waves(), 10042905305604155163); + assert_eq(fold_rest(), 4489942338656569774); + assert_eq(fold_late(), 14396952515047338484); + assert_eq(fold_late_aperture(), 14624395582691900021); + assert_eq(fold_moved(), 1950012138477754938); + // the tables' origin is the offset, the charge rises with the gate everywhere, and the sample positions of the two crossings are the deck's times + assert_eq(QG_MID[0], 2097152); + var k : usize = 0; + while (k < 625) { + assert(QG_LO[k] < QG_MID[k]); + assert(QG_MID[k] < QG_HI[k]); + k = k + 1; + } + assert(fabs64(T01_K - (T01_NS - T0_NS) / SAMPLE_NS) < 0.000001); + assert(fabs64(T075_K - (T075_NS - T0_NS) / SAMPLE_NS) < 0.001); + // the comparator's tail node starts at 0.654 V, the drain at the rail, the gates held at 0.742 V + assert(fabs64(WAVE_N1[0] - 0.654226) < 0.000001); + assert(fabs64(WAVE_AP[0] - 1.500045) < 0.000001); + // the aperture's response is linear in the step: 0.5 mV, 1 mV and 2 mV give the same response per millivolt to 0.3 percent + const r1 : f64 = AP_DD0 - AP_DE0; + const r05 : f64 = (LIN_DD[1] - LIN_DE[1]) * 2.0; + const r2 : f64 = (LIN_DD[2] - LIN_DE[2]) / 2.0; + assert(fabs64(r05 - r1) < 0.003 * r1); + assert(fabs64(r2 - r1) < 0.003 * r1); +} + +test the_charge_pulse_from_the_tables_is_what_ngspice_integrated_and_m3cs_table_is_not { + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dq); + // at clkc = 0.75 V: 26.93 fC from the tables against 26.90 fC that ngspice integrated from the gate current, 0.1 percent + const q075 : f64 = at(&dq, T075_K); + const n075 : f64 = at(&WAVE_QG_FC, T075_K); + assert(fabs64(n075 - 26.897) < 0.001); + assert(fabs64(q075 - n075) < 0.005 * n075); + // and through the edge: the tables are within 0.11 fC of the integral at every sample, 0.07 fC from 0.1 V to 0.75 V (the pulse is 27 fC) + var worst : f64 = 0.0; + var worst_edge : f64 = 0.0; + var k : usize = 0; + while (k < NS) { + const e : f64 = fabs64(dq[k] - WAVE_QG_FC[k]); + if (e > worst) { worst = e; } + if (k >= sample_floor(T01_K) && k <= sample_floor(T075_K) && e > worst_edge) { worst_edge = e; } + k = k + 1; + } + assert(worst < 0.12); + assert(worst_edge < 0.08); + // the gate capacitance the same tables give at ngspice's own operating point (Vg 0.742, Vs 0.4, Vd 1.3 V): 2.615 fF per device against its channel + both overlaps, 2.618 fF + const c_op : f64 = (qtab_fc(&QG_HI, 0.4, 1.3) - qtab_fc(&QG_LO, 0.4, 1.3)) / QG_DVG / M_PAIR; + const c_ng : f64 = (OP_CGG_AF + OP_CGSOL_AF + OP_CGDOL_AF) / 1000.0; + assert(fabs64(c_op - c_ng) < 0.01 * c_ng); + // the pair's gate capacitance at the 0.1 V crossing is 59 fF and the pulse-weighted one the network carries is 110 + assert(fabs64(cgg_frac(T01_K) - 59.19) < 0.05); + assert(fabs64(cgg_pulse(&dq, &WAVE_N1, &WAVE_AP, T01_K, T075_K) - 109.9) < 0.1); + // M3c's table (bulk at the source, 27 C, Vgs and Vds axes) is the wrong table for this pair: 36.4 fC at clkc = 0.75 V, 35 percent more than the integral + var dm : [67]f64 = [0.0] ** 67; + pulse_dq_m3c(&WAVE_N1, &WAVE_AP, &dm); + const ratio : f64 = at(&dm, T075_K) / n075; + assert(ratio > 1.30); + assert(ratio < 1.40); +} + +test the_drivers_resistances_come_from_the_tables_and_the_idle_two_port { + // the inverter holding a rail, from M3b's 50 C tables: within 1 percent of the ohms ngspice measured on the golden inverter (sar_off_ron.cir) + assert(fabs64(r_inv_n() - RON_INVN) < 0.01 * RON_INVN); + assert(fabs64(r_inv_p() - RON_INVP) < 0.01 * RON_INVP); + assert(r_inv_n() < r_inv_p()); + // the idle bit's two tgates, in situ from the two-port: x11 2587 ohm, x2 2586 ohm, within 0.5 percent of the tgate sar_off_ron.cir measured alone + assert(fabs64(r_x11() - RON_TG) < 0.005 * RON_TG); + assert(fabs64(r_x2() - RON_TG) < 0.005 * RON_TG); + assert(fabs64(r_x11() - r_x2()) < 0.002 * r_x11()); + // its capacitances, 25.6, 12.8 and 3.2 fF at 100 MHz, agree with the 300 MHz reading to 1 percent + assert(fabs64(idle_c(0) - 25.615) < 0.01); + assert(fabs64(idle_c(2) - 12.809) < 0.01); + assert(fabs64(idle_c(1) - 3.194) < 0.01); + assert(fabs64(y_c(&Y_I300, 1, 300000000.0) - idle_c(0)) < 0.01 * idle_c(0)); + assert(fabs64(y_c(&Y_I300, 7, 300000000.0) - idle_c(2)) < 0.01 * idle_c(2)); + assert(fabs64(y_c(&Y_I300, 3, 300000000.0) - idle_c(1)) < 0.02 * idle_c(1)); + // the moved N plate is behind the inverter and one tgate more, at a rail one device again: twice the P plate's + assert(fabs64(n_drive(DR_UP) - 2.0 * r_inv_p()) < 0.000001); + assert(fabs64(p_drive(DR_DOWN) - r_inv_n()) < 0.000001); + // the tables cannot give the tgate at VCM: no body effect in a table made with the source and bulk together, so they read it 30 percent too stiff + const rt : f64 = r_tgate_tables(); + assert(rt < 0.75 * RON_TG); + assert(rt > 0.65 * RON_TG); +} + +test the_network_has_34_nodes_a_solve_that_solves_and_m1s_words_that_follow_the_f64_run { + assert_eq(NMAX, 34); + assert_eq(NSQ, NMAX * NMAX); + assert_eq(WA, 2 * NMAX + 1); + assert_eq(NAUG, NMAX * WA); + // A X = [C / dt | b] to a part in 1e9 (the stiff 1 mOhm joints leave 2e-11) + assert(base_residual() < 0.000000001); + // the words of M1's step against the f64 run of the same method: 1.2 uV at the worst plate and sample + assert(quant_err_v() < 0.000003); + assert(quant_err_v() > 0.0000001); +} + +test with_ideal_plates_the_network_is_the_capacitive_divider_and_both_plates_fall_alike { + const r : KickOut = kick_row(V_PLATES); + // closed form: stiff plates make the DAC a capacitor to ground, 31 units on the top plate and the one-unit bridge in series with the 31 of net1, plus 12 fF and the gate + var dq : [67]f64 = [0.0] ** 67; + pulse_dq(&QG_MID, &WAVE_N1, &WAVE_AP, &dq); + const c_tot : f64 = C_EXPLICIT_FF + cgg_pulse(&dq, &WAVE_N1, &WAVE_AP, T01_K, T075_K) + units_on(1) * C_UNIT_FF + C_UNIT_FF * (units_on(0) * C_UNIT_FF) / (C_UNIT_FF + units_on(0) * C_UNIT_FF); + const closed : f64 = 0.0 - dq_kick(&dq) / c_tot; + assert(fabs64(r.dvdp - closed) < 0.005 * fabs64(closed)); + // both plates fall by 6.71 mV, within 5 percent of ngspice's 6.87 mV, and by exactly the same + assert(fabs64(r.dvdp - KICK_P[V_PLATES]) < 0.05 * fabs64(KICK_P[V_PLATES])); + assert(fabs64(r.dvdn - KICK_N[V_PLATES]) < 0.05 * fabs64(KICK_N[V_PLATES])); + assert(fabs64(r.dvdp - r.dvdn) < 0.0001); + assert(r.dvdp < 0.0 - 0.0065); + // the decision's offset is the latch's own + assert(fabs64(r.off - LATCH_OFF_V) < 0.000000001); +} + +test the_golden_plates_fall_by_22_and_30_mv_and_the_n_half_falls_more { + const b : KickOut = kick_row(V_BASE); + // dVDP and dVDN at clkc = 0.75 V within 10 percent of ngspice's -22.7 and -30.2 mV (derived -22.4 and -29.5) + assert(fabs64(b.dvdp - KICK_P[V_BASE]) < 0.1 * fabs64(KICK_P[V_BASE])); + assert(fabs64(b.dvdn - KICK_N[V_BASE]) < 0.1 * fabs64(KICK_N[V_BASE])); + assert(b.dvdp < 0.0 - 0.020); + assert(b.dvdn < b.dvdp); + // the 7 mV between them, within 10 percent of ngspice's 7.5 mV + assert(fabs64((b.dvdp - b.dvdn) - kick_diff(V_BASE)) < 0.1 * kick_diff(V_BASE)); + // soft plates fall 3.3 and 4.4 times as far as stiff ones: the same windows as the measurement's + const ideal : KickOut = kick_row(V_PLATES); + const rp : f64 = b.dvdp / ideal.dvdp; + const rn : f64 = b.dvdn / ideal.dvdn; + assert(rp > 3.2); + assert(rp < 3.4); + assert(rn > 4.3); + assert(rn < 4.5); + // the tracking switch is not in the network: the ideal-switch row is the base row + assert(fabs64(kick_row(V_IDEALSW).off - b.off) < 0.000000001); +} + +test with_one_half_ideal_the_kick_differential_is_lopsided_the_way_ngspice_found { + const n : KickOut = kick_row(V_NPLATE); + const p : KickOut = kick_row(V_PPLATE); + // N half ideal: the differential within 15 percent of ngspice's -20.5 mV (derived -19.1); P half ideal: +35.9 mV (derived +33.0) + assert(fabs64((n.dvdp - n.dvdn) - kick_diff(V_NPLATE)) < 0.15 * fabs64(kick_diff(V_NPLATE))); + assert(fabs64((p.dvdp - p.dvdn) - kick_diff(V_PPLATE)) < 0.15 * fabs64(kick_diff(V_PPLATE))); + assert(n.dvdp - n.dvdn < 0.0 - 0.017); + assert(p.dvdp - p.dvdn > 0.030); + // each plate on its own: the ideal half's partner is lifted (VDN rises 2.8 mV with the N half ideal, VDP 9.8 mV with the P half ideal), the other falls + assert(n.dvdn > 0.0); + assert(p.dvdp > 0.0); + assert(fabs64(n.dvdp - KICK_P[V_NPLATE]) < 0.1 * fabs64(KICK_P[V_NPLATE])); + assert(fabs64(p.dvdn - KICK_N[V_PPLATE]) < 0.1 * fabs64(KICK_N[V_PPLATE])); +} + +test a_resistor_on_the_p_plates_takes_the_offset_through_zero_where_ngspice_found_it { + // the first decision's offset with 1300, 2592 and 3900 ohm between each P plate and its Bit node: within 1 mV of +4.09, -0.89 and -4.19 mV + var i : usize = 0; + var prev : f64 = kick_row(V_BASE).off; + while (i < NRS) { + const o : f64 = kick_series(RS_OHM[i]).off; + assert(fabs64(o - rs_off(i)) < 0.001); + assert(o < prev); + prev = o; + i = i + 1; + } + // with a tgate's own resistance the plates fall alike (29.2 and 29.1 against ngspice's 29.7 and 29.2) and the offset is under 1 mV + const t : KickOut = kick_series(RS_OHM[1]); + assert(fabs64(t.dvdp - t.dvdn) < 0.0007); + assert(fabs64(t.dvdp + 0.0297480) < 0.002); + assert(fabs64(t.off) < 0.001); + // it crosses the latch's own 0.153 mV between 2200 and 2450 ohm (2386); ngspice's 2320 is 3 percent from it + const r0 : f64 = series_cross_ohm(); + assert(r0 > 2200.0); + assert(r0 < 2450.0); + assert(fabs64(r0 - rs_zero_ohm()) < 0.04 * rs_zero_ohm()); +} + +test the_six_first_decision_rows_and_the_series_row_are_within_1_mv_and_in_the_measured_order { + var v : usize = 0; + while (v < 6) { + const o : f64 = kick_row(v).off; + assert(fabs64(o - first_off(v)) < 0.001); + v = v + 1; + } + assert(fabs64(kick_series(RS_OHM[1]).off - rs_off(1)) < 0.001); + // the order: P ideal > golden > symmetric = comparator isolated > N ideal + const pid : f64 = kick_row(V_PPLATE).off; + const base : f64 = kick_row(V_BASE).off; + const sym : f64 = kick_row(V_PLATES).off; + const iso : f64 = kick_row(V_NOKICK).off; + const nid : f64 = kick_row(V_NPLATE).off; + assert(pid > base); + assert(base > sym); + assert(fabs64(sym - iso) < 0.000000001); + assert(sym > nid); + // the signs and sizes: +43.0, +9.3, +0.15, +0.15, -24.9 mV + assert(pid > 0.042); + assert(base > 0.009); + assert(base < 0.0095); + assert(nid < 0.0 - 0.024); + // the comparator isolated (no charge pulse) leaves the latch's own offset and nothing else, against ngspice's +0.03 +- 0.15 mV + assert(fabs64(iso - LATCH_OFF_V) < 0.000000001); + assert(fabs64(LATCH_OFF_V + LATCH_FLIP_V) < 0.000000001); + assert(fabs64(iso - first_off(V_NOKICK)) < 0.00025); +} + +test the_derived_kick_follows_the_charge_pulse_and_the_drive_and_nothing_else { + // 1. no pulse, no kick: the plates stay put and the offset is the latch's + const none : KickOut = kick_with(DR_IDLE, DR_IDLE, 0.0, true, 1.0, SUB, PULSE_NONE); + assert(fabs64(none.dvdp) < 0.000000001); + assert(fabs64(none.dvdn) < 0.000000001); + // 2. M3c's gate-charge table (bulk at the source, 27 C) pushes in 35 percent more charge and the plates fall 21 percent too far: S1 fails its 10 percent + const m3c : KickOut = kick_with(DR_IDLE, DR_IDLE, 0.0, true, 1.0, SUB, PULSE_M3C); + assert(fabs64(m3c.dvdp - KICK_P[V_BASE]) > 0.15 * fabs64(KICK_P[V_BASE])); + assert(m3c.dvdp < 1.15 * KICK_P[V_BASE]); + // 3. the tgate from M3b's tables (no body effect) is 1.43 times too stiff a drive: S1 fails by 17 to 19 percent + const stiff : KickOut = kick_with(DR_IDLE, DR_IDLE, 0.0, true, r_x11() / r_tgate_tables(), SUB, PULSE_TABLE); + assert(fabs64(stiff.dvdp - KICK_P[V_BASE]) > 0.15 * fabs64(KICK_P[V_BASE])); + assert(fabs64(stiff.dvdn - KICK_N[V_BASE]) > 0.15 * fabs64(KICK_N[V_BASE])); + assert(kick_row(V_BASE).off - stiff.off > 0.0015); + // 4. the bit's two-port capacitances left out: the one-half-ideal rows move by 1.7 and 2.7 mV and the 2592 ohm row misses ngspice's -0.89 mV by more than 1 mV + const np_n : KickOut = kick_with(DR_IDLE, DR_MIRROR, 0.0, false, 1.0, SUB, PULSE_TABLE); + const np_p : KickOut = kick_with(DR_MIRROR, DR_IDLE, 0.0, false, 1.0, SUB, PULSE_TABLE); + const np_s : KickOut = kick_with(DR_IDLE, DR_IDLE, 2592.0, false, 1.0, SUB, PULSE_TABLE); + const dn : f64 = fabs64(np_n.off - kick_row(V_NPLATE).off); + const dp : f64 = fabs64(np_p.off - kick_row(V_PPLATE).off); + assert(dn > 0.0015); + assert(dn < 0.0019); + assert(dp > 0.0025); + assert(dp < 0.0029); + assert(fabs64(np_s.off - rs_off(1)) > 0.001); + assert(fabs64(np_p.off - first_off(V_PPLATE)) > 0.001); + // 5. a step of 100 ps (one per sample) moves the rows by up to 0.9 mV and the one-half-ideal rows then miss by more than 1 mV + const c_n : KickOut = kick_with(DR_IDLE, DR_MIRROR, 0.0, true, 1.0, 1, PULSE_TABLE); + const c_p : KickOut = kick_with(DR_MIRROR, DR_IDLE, 0.0, true, 1.0, 1, PULSE_TABLE); + const c_b : KickOut = kick_with(DR_IDLE, DR_IDLE, 0.0, true, 1.0, 1, PULSE_TABLE); + assert(fabs64(c_p.off - kick_row(V_PPLATE).off) > 0.0008); + assert(fabs64(c_p.off - kick_row(V_PPLATE).off) < 0.0009); + assert(fabs64(c_n.off - kick_row(V_NPLATE).off) > 0.0004); + assert(fabs64(c_b.off - kick_row(V_BASE).off) < 0.0003); + assert(fabs64(c_p.off - first_off(V_PPLATE)) > 0.001); + assert(fabs64(c_n.off - first_off(V_NPLATE)) > 0.001); + // and 20 steps per sample (dt = 5 ps) move the 10 ps result by under 0.05 mV: dt = 10 ps is converged + const f_n : KickOut = kick_with(DR_IDLE, DR_MIRROR, 0.0, true, 1.0, 20, PULSE_TABLE); + assert(fabs64(f_n.off - kick_row(V_NPLATE).off) < 0.00005); +} + +test the_top_plate_load_with_stiff_plates_is_the_tables_gate_capacitance_and_its_dependence_on_the_drive_is_the_next_gap { + // FR-003: with ideal plates the first plate step reads a top-plate load Cp of 0.582 units of 122.94 fF; the network's load is the explicit 12 fF plus the pair's gate + // capacitance at the pre-edge bias from the tables, 59.2 fF: 0.579 units, within 1 percent of the measurement + const cp : f64 = (C_EXPLICIT_FF + cgg_frac(T01_K)) / C_UNIT_FF; + const cp_ideal : f64 = cp_of_step(step_mean(V_PLATES)); + assert(fabs64(cp - cp_ideal) < 0.01 * cp_ideal); + assert(fabs64(cp - 0.5791) < 0.0005); + // what is NOT derived: the golden circuit reads 0.647 and the P plates behind 1300, 2592, 3900 ohm read 0.62, 0.69, 0.85 and a raised and a lowered plate differ by 2.85 mV + // (sar_kick.t27): the load grows by 0.07 to 0.27 units, which the fixed gate capacitance does not give. The step is read a time after the plate moves that the pinned data do not hold + assert(cp_of_step(step_mean(V_BASE)) - cp > 0.06); + assert(cp_of_step((RS_STEP_UP[0] + RS_STEP_DN[0]) / 2.0) - cp > 0.04); + assert(cp_of_step((RS_STEP_UP[2] + RS_STEP_DN[2]) / 2.0) - cp > 0.26); + assert(STEP_UP[V_BASE] - STEP_DN[V_BASE] > 0.0028); +} + +test a_moved_bits_drivers_are_the_inverters_the_tables_give_and_its_two_port_adds_capacitance_not_coupling { + // the operating points of sar_kick_cnode_moved.cir: 10 mV over the current is the path's resistance. P plate up through the pmos 1881 ohm, down through the nmos 1428 ohm; the N plate + // behind the inverter x12 and the tgate x15: 2862 ohm (down) and 3772 ohm (up), twice the inverter's, to within 1 percent + const rpu : f64 = 0.01 / MV_OP_I[0]; + const rnd : f64 = 0.0 - 0.01 / MV_OP_I[1]; + const rpd : f64 = 0.0 - 0.01 / MV_OP_I[2]; + const rnu : f64 = 0.01 / MV_OP_I[3]; + assert(fabs64(rpu - p_drive(DR_UP)) < 0.01 * rpu); + assert(fabs64(rpd - p_drive(DR_DOWN)) < 0.01 * rpd); + assert(fabs64(rnd - n_drive(DR_DOWN)) < 0.01 * rnd); + assert(fabs64(rnu - n_drive(DR_UP)) < 0.01 * rnu); + assert(rpu > rpd); + assert(rnu > rnd); + // the ac conductances at 100 MHz agree with the operating point to 0.5 percent, and there is no conductance between the two plates (x11 is off) + assert(fabs64(1000000.0 / moved_g(0, 0) - rpu) < 0.005 * rpu); + assert(fabs64(1000000.0 / moved_g(1, 0) - rpd) < 0.005 * rpd); + assert(fabs64(1000000.0 / moved_g(0, 2) - rnd) < 0.005 * rnd); + assert(fabs64(1000000.0 / moved_g(1, 2) - rnu) < 0.005 * rnu); + assert(fabs64(moved_g(0, 1)) < 0.001 * moved_g(0, 0)); + assert(fabs64(moved_g(1, 1)) < 0.001 * moved_g(1, 0)); + // what its devices add: 50 and 41 fF at the P plate's node, 11 and 19 fF at the N plate's, a normal coupling of 2 fF (a negative entry); more than an idle bit's 25.6 fF at Bit + assert(fabs64(moved_c(0, 0) - 50.05) < 0.05); + assert(fabs64(moved_c(1, 0) - 41.2) < 0.1); + assert(fabs64(moved_c(0, 2) - 11.28) < 0.05); + assert(fabs64(moved_c(1, 2) - 19.35) < 0.05); + assert(moved_c(0, 1) < 0.0 - 1.9); + assert(moved_c(1, 1) < 0.0 - 1.9); + assert(moved_c(0, 0) > idle_c(0)); + // the 300 MHz reading of the P node's capacitance is within 1 percent + assert(fabs64(y_c(&Y_MV300, 1, 300000000.0) - moved_c(0, 0)) < 0.01 * moved_c(0, 0)); + assert(fabs64(y_c(&Y_MV300, 9, 300000000.0) - moved_c(1, 0)) < 0.01 * moved_c(1, 0)); +} + +test the_late_windows_are_the_sine_runs_own_edges_and_its_late_decisions_start_from_a_lower_net1 { + // the plates' VDP - VDN at the 0.1 V crossing of each window is the pinned sine run's D to 0.01 mV: the run reproduces the one that was pinned + var w : usize = 0; + while (w < LATE_NW) { + assert(fabs64(LATE_D_V[w] - DEC_D[(LATE_RISE[w] as usize) - 1]) < 0.00001); + // each window starts 1.50 to 1.55 ns before the crossing and the clock takes 3.7 to 3.9 ns from 0.1 V to 0.75 V + const lead : f64 = LATE_TCB_NS[w] - LATE_TS_NS[w]; + assert(lead > 1.499); + assert(lead < 1.551); + const rise : f64 = LATE_TCC_NS[w] - LATE_TCB_NS[w]; + assert(rise > 3.7); + assert(rise < 3.9); + w = w + 1; + } + // the sine run's first decision has net1 6 mV above the edge deck's at the window start, the late ones 32 to 40 mV below it + assert(LATE_N1[0] - WAVE_N1[0] > 0.006); + assert(LATE_N1[0] - WAVE_N1[0] < 0.007); + assert(WAVE_N1[0] - LATE_N1[NS] > 0.039); + assert(WAVE_N1[0] - LATE_N1[NS] < 0.041); + assert(WAVE_N1[0] - LATE_N1[4 * NS] > 0.031); + assert(WAVE_N1[0] - LATE_N1[4 * NS] < 0.033); + // the charge the pair takes between the 0.1 V and 0.75 V crossings (27.2 fC at the edge deck's first decision): 27.5 and 27.6 fC at the first conversion's late decisions, 27.3 and 27.2 at the + // second's; the pair's gate capacitance averaged over the pulse 112.8 and 111.5 fF against 109.9 + var dq : [67]f64 = [0.0] ** 67; + var n1 : [67]f64 = [0.0] ** 67; + var ap : [67]f64 = [0.0] ** 67; + window_waves(1, &n1, &ap); + pulse_dq(&QG_MID, &n1, &ap, &dq); + assert(fabs64(at(&dq, window_t075k(1)) - at(&dq, window_t01k(1)) - 27.53) < 0.01); + assert(fabs64(cgg_pulse(&dq, &n1, &ap, window_t01k(1), window_t075k(1)) - 112.8) < 0.1); + window_waves(4, &n1, &ap); + pulse_dq(&QG_MID, &n1, &ap, &dq); + assert(fabs64(at(&dq, window_t075k(4)) - at(&dq, window_t01k(4)) - 27.28) < 0.01); + assert(fabs64(cgg_pulse(&dq, &n1, &ap, window_t01k(4), window_t075k(4)) - 111.5) < 0.1); + // the decisions' bits, derived from the pinned D before each, are the pinned decisions at 7 .. 10 of both conversions + var cv : usize = 0; + while (cv < 2) { + var i : usize = 7; + while (i < 11) { + assert_eq(sine_bit(cv, i) as u32, LD_BIT[4 * cv + i - 7]); + i = i + 1; + } + cv = cv + 1; + } +} + +test the_late_apertures_are_measured_at_the_late_state_and_differ_from_the_first_decisions { + // the unperturbed run of the scan has the 0.1 V crossing of sar_kick_late.cir to 2.1 ps (the series source and a different step history) + assert(fabs64(LAP_TCB_NS[0] - LATE_TCB_NS[1]) < 0.003); + assert(fabs64(LAP_TCB_NS[1] - LATE_TCB_NS[4]) < 0.003); + // the response to a 1 mV step before the edge is 24.9 and 25.8 mV at the late decisions against 36.3 mV at the first: the late latch has 0.69 and 0.71 of its gain at the read instant + const g1 : f64 = (AP_DD0 - AP_DE0) - (LIN_DD[0] - LIN_DE[0]); + const g2 : f64 = (LAP_DD0[0] - LAP_DE0[0]) - (LAP_LIN_DD[0] - LAP_LIN_DE[0]); + const g3 : f64 = (LAP_DD0[1] - LAP_DE0[1]) - (LAP_LIN_DD[3] - LAP_LIN_DE[3]); + assert(fabs64(g1 - 0.036336) < 0.00001); + assert(fabs64(g2 - 0.024864) < 0.00001); + assert(fabs64(g3 - 0.025759) < 0.00001); + // linear in the step to 1 percent (steps of 0.5, 1 and 2 mV, each less the unperturbed run's own output difference, which is the decision's margin, not zero) + const b2 : f64 = LAP_LIN_DD[0] - LAP_LIN_DE[0]; + const b3 : f64 = LAP_LIN_DD[3] - LAP_LIN_DE[3]; + assert(fabs64(((LAP_LIN_DD[1] - LAP_LIN_DE[1]) - b2) * 2.0 - g2) < 0.01 * g2); + assert(fabs64(((LAP_LIN_DD[2] - LAP_LIN_DE[2]) - b2) / 2.0 - g2) < 0.01 * g2); + assert(fabs64(((LAP_LIN_DD[4] - LAP_LIN_DE[4]) - b3) * 2.0 - g3) < 0.01 * g3); + assert(fabs64(((LAP_LIN_DD[5] - LAP_LIN_DE[5]) - b3) / 2.0 - g3) < 0.01 * g3); + // each share runs from 1 to 0 and falls at every row but the last two, which are within 0.001 of zero + var set : usize = 0; + while (set < 3) { + assert(ap_share(set, 0) > 0.98); + assert(fabs64(ap_share(set, 15)) < 0.000001); + assert(fabs64(ap_share(set, 14)) < 0.001); + var j : usize = 0; + while (j < 13) { + assert(ap_share(set, j) > ap_share(set, j + 1)); + j = j + 1; + } + set = set + 1; + } + // the late latch's share is already 0.985 at 3.0 ns where the first decision's is 1.000, and at 4.0 ns it is 0.555 where the first's is 0.601; they meet at 4.5 ns (0.075 and 0.073) + assert(fabs64(ap_share(1, 0) - 0.9851) < 0.0001); + assert(fabs64(ap_share(2, 0) - 0.9854) < 0.0001); + assert(fabs64(ap_share(0, 0) - 0.9995) < 0.0001); + assert(fabs64(ap_share(1, 8) - 0.5554) < 0.0001); + assert(fabs64(ap_share(2, 8) - 0.5562) < 0.0001); + assert(fabs64(ap_share(0, 8) - 0.6005) < 0.0001); + assert(fabs64(ap_share(1, 12) - 0.0754) < 0.0001); + assert(fabs64(ap_share(0, 12) - 0.0733) < 0.0001); +} + +test decisions_9_and_10_with_their_own_edge_and_aperture_are_within_0_2_mv_of_the_measured_late_offsets_in_three_of_four_and_s5_is_not_met { + // the measured late offsets (sar_kick.t27: 28 decisions at each index, one threshold each): decision 9 6.6575 mV to 0.05 mV; decision 10 6.895 mV, which the bit path moves between 6.85 and 7.06 mV + const m9 : f64 = 0.0 - late_thr(9).mid; + const m10 : f64 = 0.0 - late_thr(10).mid; + assert(fabs64(m9 - 0.0066575) < 0.000001); + assert(fabs64(m10 - 0.006895) < 0.000001); + // the network's, each decision with its own edge (pulse from the tables) and its own aperture, the plates it had moved held by their inverters: 6.830, 6.862, 6.631 and 6.604 mV + const o90 : f64 = late_off(0, 9); + const o100 : f64 = late_off(0, 10); + const o91 : f64 = late_off(1, 9); + const o101 : f64 = late_off(1, 10); + assert(fabs64(o90 - 0.0068295) < 0.000002); + assert(fabs64(o100 - 0.0068615) < 0.000002); + assert(fabs64(o91 - 0.0066305) < 0.000002); + assert(fabs64(o101 - 0.0066035) < 0.000002); + // the issue's 0.2 mV: met by the first conversion's two decisions (+0.17 and -0.03 mV) and the second's ninth (-0.03 mV) ... + assert(fabs64(o90 - m9) < 0.0002); + assert(fabs64(o100 - m10) < 0.0002); + assert(fabs64(o91 - m9) < 0.0002); + // ... and NOT by the second conversion's tenth: 6.604 against 6.895 mV, 0.29 mV low, and 0.25 mV under the lowest the paths allow (6.85). S5 is not met; the gap is stated, not closed. + const gap : f64 = o101 - m10; + assert(gap < 0.0 - 0.0002); + assert(gap > 0.0 - 0.0003); + assert(o101 < 0.00685 - 0.0002); + // what the network does not have: the measured offset has one value at decision 9 over every path (28 decisions inside 0.05 mV), the derived has two 0.2 mV apart; and the measured offset + // is 0.24 mV higher at decision 10 than at 9 where the derived is the same (to 0.03 mV) + assert(fabs64(o90 - o91) > 0.0001); + assert(fabs64(o90 - o91) < 0.0003); + assert(m10 - m9 > 0.0002); + assert(fabs64(o100 - o90) < 0.00005); + assert(fabs64(o101 - o91) < 0.00005); + // the mean of the four, 6.731 mV, against the 6.776 mV the converter model takes (sar_adc.t27 is not switched to it) + assert(fabs64(late_off_derived() - 0.0067315) < 0.000002); + assert(late_off_derived() > 0.0066); + assert(late_off_derived() < 0.0069); +} + +test the_late_decisions_controls_the_first_decisions_edge_the_bits_capacitances_and_the_bit_path_each_move_the_offset_by_a_tenth_to_a_third_of_a_millivolt { + const m9 : f64 = 0.0 - late_thr(9).mid; + const m10 : f64 = 0.0 - late_thr(10).mid; + // 1. the first decision's edge and aperture (the edge deck's, as the first lane read the late decisions): 7.087, 7.086, 6.774 and 6.772 mV; the first conversion's ninth misses by 0.43 mV + assert(fabs64(late_kick(0, 9, LATE_NW).off - 0.0070875) < 0.000002); + assert(fabs64(late_kick(0, 10, LATE_NW).off - 0.0070865) < 0.000002); + assert(fabs64(late_kick(1, 9, LATE_NW).off - 0.0067745) < 0.000002); + assert(fabs64(late_kick(1, 10, LATE_NW).off - 0.0067725) < 0.000002); + assert(late_kick(0, 9, LATE_NW).off - m9 > 0.0004); + // 2. the late edge with the first decision's aperture: 6.980, 7.009, 6.796 and 6.769 mV: the aperture's timing matters at 0.1 to 0.2 mV + assert(fabs64(late_kick_opts(0, 9, 1, 0, true).off - 0.0069805) < 0.000002); + assert(fabs64(late_kick_opts(1, 9, 4, 0, true).off - 0.0067965) < 0.000002); + assert(late_kick_opts(0, 9, 1, 0, true).off - late_off(0, 9) > 0.0001); + // 3. the bits' capacitances left out (idle two-port and moved two-port): +0.11, +0.11, +0.05 and +0.05 mV + const np90 : f64 = late_kick_opts(0, 9, 1, 1, false).off; + const np101 : f64 = late_kick_opts(1, 10, 5, 2, false).off; + assert(np90 - late_off(0, 9) > 0.0001); + assert(np90 - late_off(0, 9) < 0.00012); + assert(np101 - late_off(1, 10) > 0.00004); + assert(np101 - late_off(1, 10) < 0.00006); + // 4. the bit path: the first conversion's plates under the second's edge and aperture, and the reverse, differ by 0.3 mV from the plates that belong to them + assert(fabs64(late_kick_opts(1, 9, 1, 1, true).off - 0.0065365) < 0.000002); + assert(fabs64(late_kick_opts(0, 9, 4, 2, true).off - 0.0069365) < 0.000002); + assert(late_kick_opts(0, 9, 1, 1, true).off - late_kick_opts(1, 9, 1, 1, true).off > 0.0002); + assert(m10 > m9); +} + +test the_first_decision_read_from_the_sine_runs_own_edge_stays_within_1_mv_and_sits_0_6_mv_above_the_edge_decks { + // the edge deck holds the two gates on ideal sources settled for 14 ns; the sine run's first decision (window 0, plates 31 mV apart: the pulse is read from the table at one gate level for both) has + // net1 6 mV higher and gives 9.97 mV against the edge deck's 9.34 and ngspice's 9.42 (the base experiment, D near the flip) + const own : f64 = late_kick(0, 0, 0).off; + assert(fabs64(own - first_off(V_BASE)) < 0.001); + assert(own - kick_row(V_BASE).off > 0.0005); + assert(own - kick_row(V_BASE).off < 0.0007); +} + +test with_each_gate_at_its_own_level_every_kick_is_within_3_5_percent_and_the_three_differentials_are_closer_than_with_one_pulse { + // the extension the three gate-level tables allow: VDP's device takes the charge of its own gate (0.742 V + D / 2), VDN's of its own (0.742 V - D / 2), D being the VDP - VDN at which + // ngspice measured the kick (the input just below the flip). The charge rises 81 fC per volt of gate: a plate 4.7 mV lower takes 0.4 fC (1.4 percent) less. Every kick of the four + // experiments with a kick is then within 3.5 percent of ngspice's (one pulse: 1.3 to 8 percent) + var v : usize = 0; + while (v < NV_ROWS) { + const vv : usize = row_of(v); + const r : KickOut = kick_row_g(vv, FLIP_LO[vv]); + assert(fabs64(r.dvdp - KICK_P[vv]) < 0.035 * fabs64(KICK_P[vv])); + assert(fabs64(r.dvdn - KICK_N[vv]) < 0.035 * fabs64(KICK_N[vv])); + v = v + 1; + } + // the three differentials: golden 7.40 mV against 7.46 (0.8 percent; one pulse 7.15, 4.1), N ideal -19.9 against -20.5 (2.6; 6.6), P ideal +35.0 against +35.9 (2.7; 8.1) + const b : KickOut = kick_row_g(V_BASE, FLIP_LO[V_BASE]); + const n : KickOut = kick_row_g(V_NPLATE, FLIP_LO[V_NPLATE]); + const p : KickOut = kick_row_g(V_PPLATE, FLIP_LO[V_PPLATE]); + const b1 : KickOut = kick_row(V_BASE); + const n1 : KickOut = kick_row(V_NPLATE); + const p1 : KickOut = kick_row(V_PPLATE); + assert(fabs64((b.dvdp - b.dvdn) - kick_diff(V_BASE)) < 0.01 * kick_diff(V_BASE)); + assert(fabs64((n.dvdp - n.dvdn) - kick_diff(V_NPLATE)) < 0.03 * fabs64(kick_diff(V_NPLATE))); + assert(fabs64((p.dvdp - p.dvdn) - kick_diff(V_PPLATE)) < 0.03 * fabs64(kick_diff(V_PPLATE))); + assert(fabs64((b.dvdp - b.dvdn) - kick_diff(V_BASE)) < fabs64((b1.dvdp - b1.dvdn) - kick_diff(V_BASE))); + assert(fabs64((n.dvdp - n.dvdn) - kick_diff(V_NPLATE)) < fabs64((n1.dvdp - n1.dvdn) - kick_diff(V_NPLATE))); + assert(fabs64((p.dvdp - p.dvdn) - kick_diff(V_PPLATE)) < fabs64((p1.dvdp - p1.dvdn) - kick_diff(V_PPLATE))); +} + +/// The four experiments with a kick, in the order of the table: V_BASE, V_PLATES, V_NPLATE, V_PPLATE. +pub const NV_ROWS : usize = 4; +pub fn row_of(i: usize) -> usize { + if (i == 0) { return V_BASE; } + if (i == 1) { return V_PLATES; } + if (i == 2) { return V_NPLATE; } + return V_PPLATE; +} + +test with_the_gates_at_their_own_levels_the_offsets_are_within_1_mv_but_the_p_half_ideal_row_which_is_0_15_mv_beyond { + // the offsets with the plates at the D the decision flips at (the fixed point d = -off, so the measured flip is no input): 9.600, 0.157, -25.714 and +45.016 mV against 9.42, 0.27, -25.79 and +43.87: + // the first three within 0.2 mV and the P half ideal 1.15 mV above ngspice's, 0.15 mV past the issue's 1 mV (one pulse: -0.84). The kick it comes from is 2.7 percent low. + assert(fabs64(off_row_g(V_BASE) - 0.0096) < 0.000002); + assert(fabs64(off_row_g(V_PLATES) - 0.000157) < 0.000002); + assert(fabs64(off_row_g(V_NPLATE) + 0.025714) < 0.000002); + assert(fabs64(off_row_g(V_PPLATE) - 0.045016) < 0.000002); + assert(fabs64(off_row_g(V_BASE) - first_off(V_BASE)) < 0.0002); + assert(fabs64(off_row_g(V_PLATES) - first_off(V_PLATES)) < 0.001); + assert(fabs64(off_row_g(V_NPLATE) - first_off(V_NPLATE)) < 0.0002); + assert(off_row_g(V_PPLATE) - first_off(V_PPLATE) > 0.001); + assert(off_row_g(V_PPLATE) - first_off(V_PPLATE) < 0.0013); + // the order is kept + assert(off_row_g(V_PPLATE) > off_row_g(V_BASE)); + assert(off_row_g(V_BASE) > off_row_g(V_PLATES)); + assert(off_row_g(V_PLATES) > off_row_g(V_NPLATE)); + // the series rows, at their own flips: +4.185, -0.567, -4.949 mV against +4.09, -0.89 and -4.19: within 1 mV + var i : usize = 0; + while (i < NRS) { + var d : f64 = 0.0; + var k : u32 = 0; + while (k < 4) { + d = 0.0 - kick_with_g(DR_IDLE, DR_IDLE, RS_OHM[i], d).off; + k = k + 1; + } + assert(fabs64((0.0 - d) - rs_off(i)) < 0.001); + i = i + 1; + } + // the ratio of the offset to the 0.75 V kick differential is the aperture's: 1.30 in the model, 1.26 measured, for the golden and the N half ideal + const rb : f64 = off_row_g(V_BASE) / (kick_row_g(V_BASE, FLIP_LO[V_BASE]).dvdp - kick_row_g(V_BASE, FLIP_LO[V_BASE]).dvdn); + assert(rb > 1.28); + assert(rb < 1.31); + assert(first_off(V_BASE) / kick_diff(V_BASE) < 1.27); +} + +test with_the_gates_at_their_own_levels_the_late_offsets_are_7_00_7_08_6_90_and_6_82_mv_two_of_four_within_0_2_mv { + const m9 : f64 = 0.0 - late_thr(9).mid; + const m10 : f64 = 0.0 - late_thr(10).mid; + const g90 : f64 = late_kick_g(0, 9, 1).off; + const g100 : f64 = late_kick_g(0, 10, 2).off; + const g91 : f64 = late_kick_g(1, 9, 4).off; + const g101 : f64 = late_kick_g(1, 10, 5).off; + assert(fabs64(g90 - 0.0069985) < 0.000002); + assert(fabs64(g100 - 0.0070805) < 0.000002); + assert(fabs64(g91 - 0.0068985) < 0.000002); + assert(fabs64(g101 - 0.0068235) < 0.000002); + // against the measured 6.6575 mV (decision 9) and 6.895 mV (decision 10): +0.34, +0.19, +0.24, -0.07 mV: the first conversion's tenth and the second's eleventh are within the issue's 0.2 mV, + // the other two are not + assert(g90 - m9 > 0.0002); + assert(fabs64(g100 - m10) < 0.0002); + assert(g91 - m9 > 0.0002); + assert(fabs64(g101 - m10) < 0.0002); + // what moves them from the single pulse's 6.83, 6.86, 6.63, 6.60: the plates' common mode (0.7413 V in the first conversion, 0.7487 V in the second, against the table's 0.742 V) and + // their difference (the P gate 5 to 7 mV below the N gate takes 0.4 to 0.6 fC less); the second conversion's ninth decision, with the common mode alone: +0.04 mV, with the difference alone +0.22 mV + const cm : f64 = (LATE_VP_V[4] + LATE_VN_V[4]) / 2.0; + const only_cm : f64 = late_kick_gv(1, 9, 4, cm, cm).off; + const only_d : f64 = late_kick_gv(1, 9, 4, VG_PLATES + LATE_D_V[4] / 2.0, VG_PLATES - LATE_D_V[4] / 2.0).off; + assert(only_cm - late_off(1, 9) > 0.00003); + assert(only_cm - late_off(1, 9) < 0.00006); + assert(only_d - late_off(1, 9) > 0.0002); + assert(only_d - late_off(1, 9) < 0.00024); + // neither variant meets S5: the single pulse is 3 of 4, the gate levels 2 of 4, and the model's two bit paths stay 0.2 to 0.3 mV apart at decision 9 where ngspice's 28 are within 0.05 + assert(fabs64(g90 - g91) > 0.0001); + assert(g90 - late_off(0, 9) > 0.0001); +}