diff --git a/.trinity/seals/analog_AnalogSarAdc.json b/.trinity/seals/analog_AnalogSarAdc.json index f385d47e2f..2bfc555371 100644 --- a/.trinity/seals/analog_AnalogSarAdc.json +++ b/.trinity/seals/analog_AnalogSarAdc.json @@ -8,26 +8,30 @@ { "spec": "specs/analog/sar_cdac.t27", "spec_hash": "sha256:a7c45039214a8b2e92333a88befc95af33ff482ce08af3be97e2c5b8be3eacd3" + }, + { + "spec": "specs/analog/sar_kick.t27", + "spec_hash": "sha256:ccea2477f8ea8f09a40c15a4881c9003111866a176565d60102b6f9550a7d94c" } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:c8621c9cb85a4f0990075a906c901964df0b4c48d37206057ec1d4265ccdea30", - "gen_hash_rust": "sha256:f63529e07003b67b4d34a5a999c0095daae6732a8c3e56093dcb17fcf9573947", - "gen_hash_verilog": "sha256:3281bec146dd8f240de65ffc8a867647645c8a6af1c56303eba2e7fe35289005", - "gen_hash_zig": "sha256:9fa3aa4789bcb6d02177add93c6613f6720c4f7c59b2e6b3e65b3accf66b7048", + "gen_hash_c": "sha256:c7975a48e25986b2272d521ab304e55bd417bcf7cf7d4ab3cb7aa65d96cd7150", + "gen_hash_rust": "sha256:ae7e4c0b49b85a469c57b69f3c625ce500cf1c6a00181b10f27856dbcddf592a", + "gen_hash_verilog": "sha256:a4c327a14579be4a4e3f863b98a605adf06f0d7b23808fdbaf643d74d52cf7fd", + "gen_hash_zig": "sha256:b925acb2621b537ebcb0f0b0f126ec15015820ddcb4530819bfcf91f31f88a92", "module": "AnalogSarAdc", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T05:44:03Z", + "sealed_at": "2026-10-19T08:20:17Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:48e8825a4a6a7fc24ce23472bcba5499dcafbd399ac45a14fb153a0a020018e2", + "spec_hash": "sha256:208a64705a1d07ff14d3131d999135a3d5da6c7cb9bc2e919430d7e60b34db68", "spec_path": "specs/analog/sar_adc.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 10, - "total": 10, + "passed": 15, + "total": 15, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/analog_AnalogSarKick.json b/.trinity/seals/analog_AnalogSarKick.json new file mode 100644 index 0000000000..ce7c34d599 --- /dev/null +++ b/.trinity/seals/analog_AnalogSarKick.json @@ -0,0 +1,33 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", + "closure": [ + { + "spec": "specs/analog/linear.t27", + "spec_hash": "sha256:3ddef7be183aba583ed285334ee389b48a65b41335e98914514235db7c4e5d62" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:eadf7a5711ab26d9ccc62014db5742e9384ce4f4b435056d1491aeaf4401c8eb", + "gen_hash_rust": "sha256:5516883b9db0b8b604aafaa502a79c08d8680e7be9b7a4aa05fcf61666eeea4f", + "gen_hash_verilog": "sha256:4fcae5993b32996b83ceafc7794fcb6ce084b37c79d413c5412ca602c9b8da90", + "gen_hash_zig": "sha256:fb3ac67bc0ab4942a221bf1c76792d28c313975f4e99694563d6ab56192353c9", + "module": "AnalogSarKick", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-19T08:09:24Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:ccea2477f8ea8f09a40c15a4881c9003111866a176565d60102b6f9550a7d94c", + "spec_path": "specs/analog/sar_kick.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 10, + "total": 10, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/analog/decks/sar_off_base.cir b/specs/analog/decks/sar_off_base.cir new file mode 100644 index 0000000000..597ccbbf29 --- /dev/null +++ b/specs/analog/decks/sar_off_base.cir @@ -0,0 +1,155 @@ +The golden SAR ADC with the sine or a DC input: the control of the M4e-offset experiments (#10888) +* specs/analog/decks/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 top level wired as the +* golden top wires it and its 16 subcircuits from the run-time file SAR_GOLDEN_SUBCKTS. Nothing in the circuit is changed: this is the control the other sar_off_*.cir decks (idealsw, nokick, nplate, pplate, plates, pseries) each differ from by +* ONE thing, named in their headers. It runs the golden sine (the first two conversions) or a DC input. +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* THE EXPERIMENTS (#10888; the first decision of a conversion, DC input; flip point = the D between the two inputs that bracket it, offset = minus its middle, +- half the bracket; +* data, estimators and tests in specs/analog/sar_kick.t27; Cp is the first plate step, two directions averaged, in units of the 9 um cap_cmim): +* deck candidate and change offset (mV) moved by (mV) dVDP / dVDN at clkc 0.75 V (mV) Cp +* sar_off_base the golden circuit +9.42 +- 0.20 -- -22.7 / -30.2 0.647 +* sar_off_idealsw (a) tracking switches ideal +9.19 +- 0.20 -0.23 +- 0.40 -23.0 / -30.6 0.640 +* sar_off_nokick (b) comparator kickback onto the plates removed (buffers) +0.03 +- 0.15 -9.38 +- 0.35 -0.06 / -0.10 0.621 +* sar_off_nplate (d1) N half's bottom plates ideal -25.79 +- 0.50 -35.20 +- 0.70 -17.1 / +3.3 0.582 +* sar_off_pplate (d2) P half's bottom plates ideal +43.87 +- 0.99 +34.45 +- 1.19 +11.1 / -24.9 0.676 +* sar_off_plates (d3) both halves' bottom plates ideal and symmetric +0.27 +- 0.25 -9.14 +- 0.45 -6.86 / -6.88 0.582 +* sar_off_pseries (d4) P plates behind 2592 ohm (a golden tgate's, sar_off_ron) -0.89 +- 0.25 -10.31 +- 0.45 -29.7 / -29.2 0.690 +* (the same with 1300 ohm: +4.09 +- 0.25; with 3900 ohm: -4.19 +- 1.00; it crosses the latch's own +0.15 mV at 2320 ohm, 10.5 percent under the tgate's 2592) +* (c) DAC settling when clkc fires: read D 3 ns and 10 ns before clkc = 0.1 V in the base sine run: under 0.1 mV and 0.31 mV from the fourth / fifth decision on. +* The latch itself flips at VDP - VDN = -0.153 mV (M4b-psp), so rows (b) and (d3) are the latch's own offset to within their brackets: the kickback IS the offset, and what makes it a +* difference between the two plates is the N half's bottom-plate drive (neg_bottom_plate_logic's tgate x11 idle, x15 moved: one transmission gate more than the P half's); row d4 shows the +* tgate is the asymmetry: a resistor of its size on the P half's plates takes the first decision's offset from +9.42 to -0.89 mV. +* The late decisions (index 9 and 10 of a conversion): 6.66 and 6.90 mV in this deck, from 26 DC conversions (sar_amp 0, sar_tstop 480n, sar_dmv in sar_kick.t27's header). +.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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_idealsw.cir b/specs/analog/decks/sar_off_idealsw.cir new file mode 100644 index 0000000000..4b27653f06 --- /dev/null +++ b/specs/analog/decks/sar_off_idealsw.cir @@ -0,0 +1,137 @@ +The golden SAR ADC with ideal tracking switches (candidate a of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_idealsw.cir -- sar_off_base.cir with ONE thing changed: the two bootstrapped tracking switches are ideal switches (500 ohm closed, open circuit open, no charge +* injection, no clock feedthrough, no gate lag; the 4 fF on their outputs stays). Result: the first decision's offset +9.19 +- 0.20 mV against +9.42 (moved by -0.23 +- 0.40): the switches are not it. +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* The table of all six experiments (offset, moved by, plate kicks, Cp) is in the header of sar_off_base.cir. +.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 +* THE ONE CHANGE (candidate a): the two bootstrapped tracking switches are ideal switches (500 ohm closed, open circuit open, no charge injection, no clock feedthrough, +* no lag of the gate); the 4 fF the golden switch hangs on its output (C2) stays. The switch's own device capacitance on VDP and VDN is gone. +S1 vinn vdn clks 0 swideal +S2 vinp vdp clks 0 swideal +.model swideal SW(vt=0.75 vh=0.05 ron=500 roff=1e15) +C2s1 vdn 0 4f +C2s2 vdp 0 4f +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(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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_nokick.cir b/specs/analog/decks/sar_off_nokick.cir new file mode 100644 index 0000000000..072de8ff03 --- /dev/null +++ b/specs/analog/decks/sar_off_nokick.cir @@ -0,0 +1,148 @@ +The golden SAR ADC with the comparator's kickback removed (candidate b of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_nokick.cir -- sar_off_base.cir with ONE thing changed: the comparator reads the top plates through two ideal unity-gain buffers, so nothing it does at the clock +* edge reaches VDP or VDN (a 55 fF capacitor on each plate stands in for the gates the input pair loaded them with). Result: the first decision's offset +0.03 +- 0.15 mV against +9.42 +* (moved by -9.38 +- 0.35), a bracket that holds the latch's own 0.153 mV: the kickback is the offset. +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* The table of all six experiments (offset, moved by, plate kicks, Cp) is in the header of sar_off_base.cir. +.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 +* THE ONE CHANGE (candidate b): the comparator reads the top plates through two ideal unity-gain buffers, so nothing it does at the clock edge (the tail node falling +* through the gate-source capacitance of the input pair, the drains falling through the gate-drain capacitance) reaches VDP or VDN. cin stands in for the gate +* capacitance the comparator's input pair loaded the plates with, so that the plates' load (and the DAC's gain) stays near the golden deck's. +.param cin=55f +Ebp vdpb 0 vdp 0 1 +Ebn vdnb 0 vdn 0 1 +Cip vdp 0 {cin} +Cin vdn 0 {cin} +x3 vocp vocn valid vdpb vdnb 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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_nplate.cir b/specs/analog/decks/sar_off_nplate.cir new file mode 100644 index 0000000000..88147adff4 --- /dev/null +++ b/specs/analog/decks/sar_off_nplate.cir @@ -0,0 +1,151 @@ +The golden SAR ADC with the N half's bottom plates ideal (candidate d1 of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_nplate.cir -- sar_off_base.cir with ONE thing changed: the N half's ten bottom plates are ideal voltage sources, the exact mirror of the P half's real plate +* about the common mode (Bit_k_n = 1.5 V - Bit_k), instead of the neg_bottom_plate_logic cells' outputs. Result: -25.79 +- 0.50 mV (moved by -35.20 +- 0.70): the kick is lopsided the other way. +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* The table of all six experiments (offset, moved by, plate kicks, Cp) is in the header of sar_off_base.cir. +.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 +* THE ONE CHANGE (candidate d, the N half): the N half's bottom plates are ideal voltage sources, the exact mirror of the P half's real plate about the common mode, +* Bit_k_n = 1.5 V - Bit_k at every instant, instead of the outputs of the neg_bottom_plate_logic cells (their tgates and inverters). The P half and the cells' loads on Bit_k stay. +Bn1 nid_1 0 V=1.5-v(bit_1) +Bn2 nid_2 0 V=1.5-v(bit_2) +Bn3 nid_3 0 V=1.5-v(bit_3) +Bn4 nid_4 0 V=1.5-v(bit_4) +Bn5 nid_5 0 V=1.5-v(bit_5) +Bn6 nid_6 0 V=1.5-v(bit_6) +Bn7 nid_7 0 V=1.5-v(bit_7) +Bn8 nid_8 0 V=1.5-v(bit_8) +Bn9 nid_9 0 V=1.5-v(bit_9) +Bn10 nid_10 0 V=1.5-v(bit_10) +x17 vdn nid_3 nid_8 nid_5 nid_6 nid_7 nid_4 nid_9 nid_10 nid_2 nid_1 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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_plates.cir b/specs/analog/decks/sar_off_plates.cir new file mode 100644 index 0000000000..bf6baa2a84 --- /dev/null +++ b/specs/analog/decks/sar_off_plates.cir @@ -0,0 +1,163 @@ +The golden SAR ADC with both halves' bottom plates ideal and symmetric (candidate d3 of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_plates.cir -- sar_off_base.cir with ONE thing changed: every bottom plate of both halves is an ideal voltage source, the P plate a hard-limited copy of the real +* DAC_control output and the N plate its exact mirror, so the two halves are driven symmetrically with zero impedance. Result: +0.27 +- 0.25 mV (moved by -9.14 +- 0.45), the latch's own +* offset again: both plates fall by the same 6.87 mV at the clock edge. +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* The table of all six experiments (offset, moved by, plate kicks, Cp) is in the header of sar_off_base.cir. +.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 +* THE ONE CHANGE (candidate d, both halves): every bottom plate of both halves is an ideal voltage source, the P plate a hard-limited copy of the real DAC_control output +* (0 V, 0.75 V or 1.5 V, 0.75 V + 0.75 V tanh(20 (Bit_k - 0.75 V)): the edge follows the real one, but it is stiff and clean) and the N plate its exact mirror, +* 1.5 V - P, so the two halves are driven symmetrically and with zero impedance. The drivers, the tgates and the cells keep their (now unloaded) outputs. +Bq1 qp_1 0 V=0.75+0.75*tanh(20*(v(bit_1)-0.75)) +Bq2 qp_2 0 V=0.75+0.75*tanh(20*(v(bit_2)-0.75)) +Bq3 qp_3 0 V=0.75+0.75*tanh(20*(v(bit_3)-0.75)) +Bq4 qp_4 0 V=0.75+0.75*tanh(20*(v(bit_4)-0.75)) +Bq5 qp_5 0 V=0.75+0.75*tanh(20*(v(bit_5)-0.75)) +Bq6 qp_6 0 V=0.75+0.75*tanh(20*(v(bit_6)-0.75)) +Bq7 qp_7 0 V=0.75+0.75*tanh(20*(v(bit_7)-0.75)) +Bq8 qp_8 0 V=0.75+0.75*tanh(20*(v(bit_8)-0.75)) +Bq9 qp_9 0 V=0.75+0.75*tanh(20*(v(bit_9)-0.75)) +Bq10 qp_10 0 V=0.75+0.75*tanh(20*(v(bit_10)-0.75)) +Br1 qn_1 0 V=1.5-v(qp_1) +Br2 qn_2 0 V=1.5-v(qp_2) +Br3 qn_3 0 V=1.5-v(qp_3) +Br4 qn_4 0 V=1.5-v(qp_4) +Br5 qn_5 0 V=1.5-v(qp_5) +Br6 qn_6 0 V=1.5-v(qp_6) +Br7 qn_7 0 V=1.5-v(qp_7) +Br8 qn_8 0 V=1.5-v(qp_8) +Br9 qn_9 0 V=1.5-v(qp_9) +Br10 qn_10 0 V=1.5-v(qp_10) +x17 vdn qn_3 qn_8 qn_5 qn_6 qn_7 qn_4 qn_9 qn_10 qn_2 qn_1 Diff_cap_DAC +x18 vdp qp_3 qp_8 qp_5 qp_6 qp_7 qp_4 qp_9 qp_10 qp_2 qp_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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_pplate.cir b/specs/analog/decks/sar_off_pplate.cir new file mode 100644 index 0000000000..525b6cbbf5 --- /dev/null +++ b/specs/analog/decks/sar_off_pplate.cir @@ -0,0 +1,151 @@ +The golden SAR ADC with the P half's bottom plates ideal (candidate d2 of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_pplate.cir -- sar_off_base.cir with ONE thing changed: the P half's ten bottom plates are ideal voltage sources, the exact mirror of the N half's real plate +* about the common mode (Bit_k = 1.5 V - Bit_k_n), instead of DAC_control's outputs. Result: +43.87 +- 0.99 mV (moved by +34.45 +- 1.19). +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) and sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* The table of all six experiments (offset, moved by, plate kicks, Cp) is in the header of sar_off_base.cir. +.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 +* THE ONE CHANGE (candidate d, the P half): the P half's bottom plates are ideal voltage sources, the exact mirror of the N half's real plate about the common mode, +* Bit_k = 1.5 V - Bit_k_n at every instant, instead of the outputs of the DAC_control drivers. The N half (neg_bottom_plate_logic, which still reads the real Bit_k) stays. +Bp1 pid_1 0 V=1.5-v(bit_1_n) +Bp2 pid_2 0 V=1.5-v(bit_2_n) +Bp3 pid_3 0 V=1.5-v(bit_3_n) +Bp4 pid_4 0 V=1.5-v(bit_4_n) +Bp5 pid_5 0 V=1.5-v(bit_5_n) +Bp6 pid_6 0 V=1.5-v(bit_6_n) +Bp7 pid_7 0 V=1.5-v(bit_7_n) +Bp8 pid_8 0 V=1.5-v(bit_8_n) +Bp9 pid_9 0 V=1.5-v(bit_9_n) +Bp10 pid_10 0 V=1.5-v(bit_10_n) +x18 vdp pid_3 pid_8 pid_5 pid_6 pid_7 pid_4 pid_9 pid_10 pid_2 pid_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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_pseries.cir b/specs/analog/decks/sar_off_pseries.cir new file mode 100644 index 0000000000..472d681ed4 --- /dev/null +++ b/specs/analog/decks/sar_off_pseries.cir @@ -0,0 +1,167 @@ +The golden SAR ADC with the P half's bottom plates behind a resistor (candidate d4 of the M4e-offset experiments, #10888) +* specs/analog/decks/sar_off_pseries.cir -- sar_off_base.cir with ONE thing changed: each of the P half's ten bottom plates is joined to its Bit_k node through a resistor rs (default 2592 ohm, +* the DC resistance of a golden tgate conducting at VCM, measured by sar_off_ron.cir). The N half's plates hang behind neg_bottom_plate_logic's tgate x11 off the same Bit_k node (idle), so with +* rs = that tgate's resistance the two halves' bottom plates are driven through the same resistance from the same node and the circuit is symmetric for an idle bit: if the extra tgate is the +* cause, the first decision's offset falls to the latch's own. Result: +9.42 mV (rs 0, the base deck), +4.09 +- 0.25 (1300 ohm), -0.89 +- 0.25 (2592 ohm), -4.19 +- 1.00 (3900 ohm); it crosses the +* latch's own +0.15 mV at 2320 ohm, 10.5 percent under the tgate's 2592 ohm. The plates fall by 29.7 and 29.2 mV at 2592 ohm (22.7 and 30.2 in the base deck). +* Paths and the run are NOT in this file: ngspice reads 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), SAR_GOLDEN_SUBCKTS (the subcircuit section of the golden deck, made at run time: +* sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)' > $SAR_GOLDEN_SUBCKTS +* the older recipe in sar_logic.t27 drops every line that BEGINS with .end, which loses each .ends), and, lower case because ngspice lower-cases the control lines it substitutes +* into: sar_dmv (the DC differential input in mV, or 0 for the sine), sar_amp (the sine's amplitude, 0.6 for the golden sine, 0 for a DC input) sar_tstop (150n for the first +* decision, 480n for one conversion, 1.1u for the sine's first two conversions) and, in this deck only, sar_rs (the series resistance in ohms, 2592 for the tgate's own). The deck writes sar_off.raw (binary) in the current directory and prints the measures below. +* Run: sar_dmv=-12 sar_amp=0 sar_tstop=150n IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b (ngspice-47, about a minute) +* The measures: D = V(VDP) - V(VDN) is read at the instant clkc crosses 0.1 V going up (vp1 - vn1, vp2 - vn2 for the first and second clock: before the clock edge has reached the +* comparator, so before its kickback); the first decision is the level the bottom plate Bit_10 settles to (b10 below 0.75 V = VDP above VDN, the latch's dp won); the flip point is the +* vp1 - vn1 at which b10 changes; vp050 - vn050 .. vp125 - vn125 are the plates at later clkc levels (the kickback); the first plate step is (vp2 - vn2) - (vp1 - vn1), +-2 x 0.75 V x K10 +* with K10 = 512 / (1023 + 32 Cp) of M4a. The golden sine's first two conversions are read from the raw file: D at each clkc rise through 0.1 V, the bit from vocn against vocp. +* THE EXPERIMENTS (#10888; the first decision of a conversion, DC input; flip point = the D between the two inputs that bracket it, offset = minus its middle, +- half the bracket; +* data, estimators and tests in specs/analog/sar_kick.t27; Cp is the first plate step, two directions averaged, in units of the 9 um cap_cmim): +* deck candidate and change offset (mV) moved by (mV) dVDP / dVDN at clkc 0.75 V (mV) Cp +* sar_off_base the golden circuit +9.42 +- 0.20 -- -22.7 / -30.2 0.647 +* sar_off_idealsw (a) tracking switches ideal +9.19 +- 0.20 -0.23 +- 0.40 -23.0 / -30.6 0.640 +* sar_off_nokick (b) comparator kickback onto the plates removed (buffers) +0.03 +- 0.15 -9.38 +- 0.35 -0.06 / -0.10 0.621 +* sar_off_nplate (d1) N half's bottom plates ideal -25.79 +- 0.50 -35.20 +- 0.70 -17.1 / +3.3 0.582 +* sar_off_pplate (d2) P half's bottom plates ideal +43.87 +- 0.99 +34.45 +- 1.19 +11.1 / -24.9 0.676 +* sar_off_plates (d3) both halves' bottom plates ideal and symmetric +0.27 +- 0.25 -9.14 +- 0.45 -6.86 / -6.88 0.582 +* (c) DAC settling when clkc fires: read D 3 ns and 10 ns before clkc = 0.1 V in the base sine run: under 0.1 mV and 0.31 mV from the fourth / fifth decision on. +* The latch itself flips at VDP - VDN = -0.153 mV (M4b-psp), so rows (b) and (d3) are the latch's own offset to within their brackets: the kickback IS the offset, and what makes it a +* difference between the two plates is the N half's bottom-plate drive (neg_bottom_plate_logic's tgate x11 idle, x15 moved: one transmission gate more than the P half's). +* The late decisions (index 9 and 10 of a conversion): 6.66 and 6.90 mV in this deck, from 26 DC conversions (sar_amp 0, sar_tstop 480n, sar_dmv in sar_kick.t27's header). +.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 +* THE ONE CHANGE (candidate d4): the P half's plates behind a resistor each +.param rs=2592 +Rps1 bit_1 pr_1 {rs} +Rps2 bit_2 pr_2 {rs} +Rps3 bit_3 pr_3 {rs} +Rps4 bit_4 pr_4 {rs} +Rps5 bit_5 pr_5 {rs} +Rps6 bit_6 pr_6 {rs} +Rps7 bit_7 pr_7 {rs} +Rps8 bit_8 pr_8 {rs} +Rps9 bit_9 pr_9 {rs} +Rps10 bit_10 pr_10 {rs} +x18 vdp pr_3 pr_8 pr_5 pr_6 pr_7 pr_4 pr_9 pr_10 pr_2 pr_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(vinp) v(vinn) v(clks) v(clkc) v(vocp) v(vocn) v(valid) v(out) +.save v(ck1) v(ck2) v(ck3) v(ck4) v(ck5) v(ck6) v(ck7) v(ck8) v(ck9) v(ck10) v(ck11) +.save v(bit_1) v(bit_2) v(bit_3) v(bit_4) v(bit_5) v(bit_6) v(bit_7) v(bit_8) v(bit_9) v(bit_10) +.save v(bit_1_n) v(bit_2_n) v(bit_3_n) v(bit_4_n) v(bit_5_n) v(bit_6_n) v(bit_7_n) v(bit_8_n) v(bit_9_n) v(bit_10_n) +.save v(out_1) v(out_2) v(out_3) v(out_4) v(out_5) v(out_6) v(out_7) v(out_8) v(out_9) v(out_10) v(out_11) +.save v(x3.dp) v(x3.dn) v(x3.x1.an) v(x3.x1.ap) v(x3.x1.net1) v(x17.net1) v(x18.net1) v(x1.net3) v(x2.net3) +.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=4 +alterparam dmv = $sar_dmv +alterparam amp = $sar_amp +alterparam rs = $sar_rs +reset +tran 0.1n $sar_tstop +meas tran tc1 when v(clkc)=0.75 rise=1 +meas tran tc2 when v(clkc)=0.75 rise=2 +meas tran vp1 find v(vdp) when v(clkc)=0.1 rise=1 +meas tran vn1 find v(vdn) when v(clkc)=0.1 rise=1 +meas tran vp2 find v(vdp) when v(clkc)=0.1 rise=2 +meas tran vn2 find v(vdn) when v(clkc)=0.1 rise=2 +meas tran vp050 find v(vdp) when v(clkc)=0.5 rise=1 +meas tran vn050 find v(vdn) when v(clkc)=0.5 rise=1 +meas tran vp075 find v(vdp) when v(clkc)=0.75 rise=1 +meas tran vn075 find v(vdn) when v(clkc)=0.75 rise=1 +meas tran vp100 find v(vdp) when v(clkc)=1.0 rise=1 +meas tran vn100 find v(vdn) when v(clkc)=1.0 rise=1 +meas tran vp125 find v(vdp) when v(clkc)=1.25 rise=1 +meas tran vn125 find v(vdn) when v(clkc)=1.25 rise=1 +meas tran b10 find v(bit_10) at=136n +meas tran b10n find v(bit_10_n) at=136n +set filetype=binary +write sar_off.raw +.endc +.end diff --git a/specs/analog/decks/sar_off_ron.cir b/specs/analog/decks/sar_off_ron.cir new file mode 100644 index 0000000000..839fa70541 --- /dev/null +++ b/specs/analog/decks/sar_off_ron.cir @@ -0,0 +1,33 @@ +The on-resistance of the golden tgate and inverter at the bottom plates' bias: the measurement behind the M4e-offset explanation (#10888) +* specs/analog/decks/sar_off_ron.cir -- how soft is a bottom plate's drive? The golden top level has two kinds of drive: DAC_control's inverter (and its tgate x2 to VCM while a bit is idle) on the +* P half, and neg_bottom_plate_logic's tgate x11 (idle) or inverter x12 plus tgate x15 (moved) on the N half, one transmission gate more. This deck measures, with the golden subcircuits tgate and +* inverter (from the run-time file SAR_GOLDEN_SUBCKTS, the IHP SG13G2 PSP103 model of IHP-Open-PDK 21133f21, mos_tt, 50 C), the DC resistance of +* a tgate conducting at VCM (pmos gate 0 V, nmos gate 1.5 V, Vdd 1.5 V), 10 mV across it at 0.75 V: R = 10 mV / I (rtg) +* an inverter holding its output at a rail: the pmos W 3 um with its input at 0 V and the output 10 mV under Vdd (rinvp), the nmos W 1 um with its input at 1.5 V and the output 10 mV +* over ground (rinvn): R = 10 mV / I +* Paths: IHP_PDK, IHP_OSDI, SAR_GOLDEN_SUBCKTS as in sar_off_base.cir. Run: ngspice -b sar_off_ron.cir (seconds); it prints rtg, rinvp, rinvn in ohms. +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib mos_tt +.include $SAR_GOLDEN_SUBCKTS +VDD vdd 0 1.5 +VG0 g0 0 0 +VG1 g1 0 1.5 +VIN tin 0 0.76 +VOUT tout 0 0.75 +XT g0 g1 tin tout vdd 0 tgate +XIP g0 vdd 0 opn inverter +VOPN opn 0 1.49 +XIN g1 vdd 0 onn inverter +VONN onn 0 0.01 +.options temp=50 +.control +pre_osdi $IHP_OSDI/psp103.osdi +pre_osdi $IHP_OSDI/psp103_nqs.osdi +op +let rtg = 0.01 / abs(i(vin)) +let rinvp = 0.01 / abs(i(vopn)) +let rinvn = 0.01 / abs(i(vonn)) +print rtg +print rinvp +print rinvn +.endc +.end diff --git a/specs/analog/sar_adc.t27 b/specs/analog/sar_adc.t27 index 99962815dc..6466e522c3 100644 --- a/specs/analog/sar_adc.t27 +++ b/specs/analog/sar_adc.t27 @@ -65,15 +65,31 @@ ; the 16 even conversions alone (all 16 match, Cp 0.626, 6.7 mV) scores the 15 odd ones it never saw 14 of 15; on the 15 odd alone (all 15, Cp 0.630, 6.9 mV) the 16 even 15 of ; 16. The optimum is narrow in the offset and broad in Cp: Cp -0.02 / +0.02 units matches 23 / 20, the offset -1 / +1 mV matches 11 / 6. ; WHAT THE TWO NUMBERS ARE. Cp is everything on the top plate that M4a does not have: the explicit 4 fF capacitors (C3, C5 on VDP, C2 in the switch: 12 fF) and the device -; capacitance of the comparator's 36-unit input device and of the switch, which this file does not decompose. off is not the comparator's offset alone. Reading the 22 decisions -; of the two conversions in the 1.1 us all-nodes run directly (the real VDP - VDN a fixed time before clkc passes half of Vdd, against the decided bit) puts the latch's decision -; point at +2.5 .. +3.1 mV for 1 ns before, +2.0 .. +2.6 mV for 0.5 ns, +3.3 .. +3.9 mV for 1.5 ns and +3.3 .. +4.0 mV for 2 ns: the kickback onto VDP and VDN at the clock -; edge (they fall by 12 and 25 mV in the first 2 ns, 45 and 81 mV by 104 ns) moves the comparator's view under its own decision. The fitted 6.7 mV may also absorb what else the -; model lacks (the plate drivers' settling, the injection of each plate switch; not separated here). Neither number is claimed beyond: with these two, a behavioural converter reproduces the codes -; of this run. The comparator and the top-plate load are what M4b and M3b at PSP103 level must supply for the offset and Cp to go away. +; capacitance of the comparator's 36-unit input device and of the switch, which this file does not decompose. off is not the comparator's offset alone: it is mostly the clock edge's +; kickback (THE OFFSET, below). The first reading of it, from the 22 decisions of the two conversions in the 1.1 us all-nodes run (the real VDP - VDN a fixed time before clkc passes +; half of Vdd, against the decided bit), put the decision point at +2.5 .. +3.1 mV for 1 ns before, +3.3 .. +4.0 mV for 2 ns: those are in this file's sign convention ("above" when +; VDP - VDN + off > 0, off = minus the flip point; M4b-psp's dynamic_comparator_top, 0.153 mV, has the SAME sign, not the opposite), and they were read INSIDE the kickback, which by +; 1 ns before half of Vdd has already moved VDP - VDN by 3.9 mV. Read before the edge (clkc at 0.1 V) the golden comparator flips at VDP - VDN = -6.7 .. -6.9 mV: the fitted 6.7 mV +; was the comparator's real view of the plates, and it was found by the fit to 0.08 mV. Cp stays a fit. +; - +; THE OFFSET (M4e-offset, #10888, sub-issue #10941; specs/analog/sar_kick.t27 has the data, the estimators and the tests; specs/analog/decks/sar_off_*.cir the experiments). Seven ngspice + PSP103 decks of the +; golden circuit change one thing each and read the first decision's flip point (an eighth, sar_off_ron.cir, measures a golden tgate's resistance): the golden circuit +9.42 mV; ideal tracking switches +9.19 (moved by -0.23 +- 0.40: the switches are not +; it); the comparator's kickback removed (buffers between the plates and the comparator) +0.03 (-9.38: it IS the offset, and what is left holds the latch's own 0.153 mV); the N half's +; bottom plates ideal -25.8 (-35.2); the P half's ideal +43.9 (+34.5); both halves' ideal and symmetric +0.27 (-9.14, again the latch's); a tgate's resistance added to the P half's plates -0.89 (-10.31). When clkc rises the comparator's tail node and the drains +; of its input pair fall and its gate capacitances push charge into the two top plates: VDP falls by 22.7 mV and VDN by 30.2 mV by half of Vdd, because the N half's bottom plates sit behind +; one more transmission gate (neg_bottom_plate_logic's x11 or x15) than the P half's and are softer at that time scale (a resistor of a golden tgate's size, 2592 ohm, added to each P plate +; takes the first decision's offset from +9.42 to -0.89 mV; with stiff plates both fall by 6.87 mV and nothing is left). +; The difference, 7.5 mV, plus the latch's regeneration margin, is the offset: 9.42 mV at the first decision, 6.66 mV (decision 9) and 6.90 mV (decision 10) at the last ones, from 26 DC +; conversions that put those decisions' D at 0.2 mV steps around their flip points. THE MODEL TAKES the mean of the last two, 6.776 mV (sar_kick.t27 off_late_v), for every decision: +; the decisions from the first to the fourth are never within 3 mV of their thresholds in the 31 conversions (the closest is 3.01 mV, a test), so their 2.6 mV larger value cannot change a code. +; row 4 the merged blocks, Cp = 0.63 (row 2's), off = 6.776 mV MEASURED: 29 of 31 match; the misses are conversions 16 (+1 code) and 25 (-1 code). The fitted 6.7 mV is 0.08 mV from the measured offset. +; row 5 the same with Cp re-fitted ALONE on row 2's grid, the offset fixed: 30 of 31 at Cp = 0.634. Only Cp is fitted. +; The count over the measured spread of the offset (Cp = 0.63): 6.6 mV 29, 6.655 29, 6.7 30, 6.9 30, 7.0 25, 7.1 21. The flip point depends on the bit path at the 0.1 mV level (decision 10's +; threshold lies between -7.06 and -6.85 mV depending on the path), which is the size of what separates 29 from 30. What is NOT done: the kick is measured, not derived from the plates' drive +; (the follow-up, #10942): and Cp is not predicted (the first plate step gives Cp from 0.58 to 0.85 units depending on how the plates are driven). ; - ; THE LEVELS (FR-001). Reference: the whole deck at PSP103 (OSDI) level. Sampling: PSP103, taken from the reference run. DAC: behavioural (ideal capacitors, exact charge -; redistribution) plus one fitted parasitic. Comparator: behavioural (a sign and a fitted offset); the square-law M4b is not called. SAR logic: behavioural sequence. The tracking +; redistribution) plus one fitted parasitic. Comparator: behavioural (a sign and an offset, fitted in row 2, measured on the netlist in rows 4 and 5); the square-law M4b is not called. SAR logic: behavioural sequence. The tracking ; switch of M4c is square law and is not called either: its lag at the sine's zero crossing is 1.27 mV, and this deck's held plates sit 2.5 mV from the 84 ns input there ; (conversion 16, slope 0.236 mV/ns, injection included): not the same quantity, but the level of the switch matters at the size of a few LSB. ; - @@ -88,7 +104,7 @@ ; the first decision; the ideal converter is the exact quantizer; a changed bridge, unit count, parasitic or offset is caught. ; WHAT IT DOES NOT SAY. Nothing about speed: no timing is modelled, the reference took 65 minutes and the behavioural converter does 31 conversions in a fraction of a second, ; which is not a comparison of the same thing. Nothing about power, noise, mismatch, supply or temperature, or about another corner: one run, TEMP 50 C, mos_tt, cap_typ. -; Nothing about the comparator as a circuit: the sign and offset are a stated behaviour fitted to the codes, not M4b's latch, and the offset's cause is not found. Nothing about +; Nothing about the comparator as a circuit: the sign and offset are a stated behaviour, not M4b's latch; the offset's cause is found and measured (THE OFFSET), not derived. Nothing about ; the switch: the samples are the run's, not M4c's. Nothing about the SAR logic as gates (M4d). Nothing about conversion 0 beyond its code (it is the .ic transient). The fit ; uses the 31 codes it is then scored on: row 2 is a description of this run, only the held-out rows are predictions, and 15 and 16 conversions of one sine are few. Nothing ; about a board: no bitstream, no clock, no LUT, DSP or BRAM. @@ -99,6 +115,7 @@ module AnalogSarAdc; use analog::linear::{NEG, MAG, q_sm, sm_f64, pack, is_neg, add_sm, fabs64}; use analog::sar_cdac::{UNITS, SIDE, PLATES, K_Q, derive_k_top, event_k, plate_step}; +use analog::sar_kick::{off_late_v, first_off, V_BASE, LATCH_FLIP_V}; pub const KIND : str = "analog-cell"; pub const ID : str = "analog/sar_adc"; @@ -414,6 +431,26 @@ pub fn fit_grid(par: u32, vp: []const f64, vn: []const f64) -> Fit { return best; } +/// Cp alone: the grid optimum (Cp = FIT_CP0 + FIT_DCP a, a = 0 .. 40) of the matches for a fixed offset in volts, ties broken as fit_grid breaks them. +pub fn fit_cp(off: f64, vp: []const f64, vn: []const f64) -> Fit { + var best : Fit = Fit { cp: 0.0, off: off, hits: 0, viol: 1000000.0 }; + const w : u32 = q_sm(off); + var a : usize = 0; + while (a < FIT_NA) { + const cp : f64 = FIT_CP0 + FIT_DCP * (a as f64); + const k : [20]u32 = model_k(&UNITS, 1.0, cp); + const hits : u32 = match_count_p(&k, w, vp, vn, 2); + if (hits >= best.hits) { + const v : f64 = miss_margins(&k, w, vp, vn, 2).sum; + if (hits > best.hits || v < best.viol) { + best = Fit { cp: cp, off: off, hits: hits, viol: v }; + } + } + a = a + 1; + } + return best; +} + ; --- the tests ------------------------------------------------------------------------------------------------------------ test the_ideal_sar_reads_every_bin_centre { @@ -710,3 +747,83 @@ test one_changed_block_breaks_the_match { } assert(wrong_bins >= 8); } + +test thirty_one_conversions_29_matches_with_the_offset_measured_on_the_kick_and_not_fitted { + const off : u32 = q_sm(off_late_v()); + const kf : [20]u32 = model_k(&UNITS, 1.0, CP_FIT); + // row 4: the offset is the mean of the last two decisions' flip points measured on the netlist (sar_kick.t27), Cp is row 2's + assert_eq(match_count(&kf, off, &S_VP, &S_VN), 29); + var i : usize = 0; + var held : u32 = 0; + while (i < N_CONV) { + const c : u32 = convert_sar(&kf, q_sm(S_VP[i]), q_sm(S_VN[i]), off); + if (c != NG_CODE[i]) { + // the two misses are one code off: conversion 16 one high, conversion 25 one low + assert(i == 16 || i == 25); + if (i == 16) { assert_eq(c, NG_CODE[i] + 1); } + if (i == 25) { assert_eq(c + 1, NG_CODE[i]); } + } + if (i >= 2 && c == NG_CODE[i]) { held = held + 1; } + i = i + 1; + } + // the offset's decisions are the sine's conversions 0 and 1 and DC conversions: conversions 2 .. 30 never entered it, and 27 of those 29 match + assert_eq(held, 27); + // the fitted offset found the measured one to 0.08 mV + assert fabs64(OFF_FIT_V - off_late_v()) < 0.0001; + assert(off_late_v() > OFF_FIT_V); +} + +test thirty_one_conversions_30_matches_with_the_offset_measured_and_only_cp_refitted { + // row 5: Cp alone re-fitted on row 2's grid with the offset fixed at the measured value: 30 of 31 at Cp = 0.634 (row 2's was 0.630), the offset is nobody's fit + const f : Fit = fit_cp(off_late_v(), &S_VP, &S_VN); + assert_eq(f.hits, 30); + assert fabs64(f.cp - 0.634) < 0.000000001; + assert fabs64(f.off - off_late_v()) < 0.000000001; + // the same count as the fitted row, 0.004 units (0.5 fF) of Cp away from it + assert fabs64(f.cp - CP_FIT) < 0.0041; + const kf : [20]u32 = model_k(&UNITS, 1.0, f.cp); + assert_eq(match_count(&kf, q_sm(off_late_v()), &S_VP, &S_VN), 30); +} + +test the_count_over_the_measured_spread_of_the_offset_is_29_to_30_from_6_6_to_6_9_mv_and_falls_beyond { + const kf : [20]u32 = model_k(&UNITS, 1.0, CP_FIT); + assert_eq(match_count(&kf, q_sm(0.0066), &S_VP, &S_VN), 29); + assert_eq(match_count(&kf, q_sm(0.006655), &S_VP, &S_VN), 29); + assert_eq(match_count(&kf, q_sm(0.0067), &S_VP, &S_VN), 30); + assert_eq(match_count(&kf, q_sm(0.0069), &S_VP, &S_VN), 30); + assert_eq(match_count(&kf, q_sm(0.0070), &S_VP, &S_VN), 25); + assert_eq(match_count(&kf, q_sm(0.0071), &S_VP, &S_VN), 21); +} + +test the_first_four_decisions_are_never_within_3_mv_of_their_thresholds_so_their_larger_offset_cannot_change_a_code { + const kf : [20]u32 = model_k(&UNITS, 1.0, CP_FIT); + const off : u32 = q_sm(off_late_v()); + var k : usize = 0; + var closest : f64 = 1000.0; + while (k < N_CONV) { + var res : [11]f64 = [0.0] ** 11; + var wrong : [11]u32 = [0] ** 11; + const c : u32 = sar_core(&kf, q_sm(S_VP[k]), q_sm(S_VN[k]), off, false, 0, &res, &wrong); + assert_eq(c, convert_sar(&kf, q_sm(S_VP[k]), q_sm(S_VN[k]), off)); + var i : usize = 0; + while (i < 4) { + const m : f64 = fabs64(res[i]); + if (m < closest) { closest = m; } + i = i + 1; + } + k = k + 1; + } + assert(closest > 0.0029); + assert(closest < 0.0031); + // the first decision's own offset is 2.6 mV larger than the late one: less than the closest of those four decisions to its threshold + assert(first_off(V_BASE) - off_late_v() < closest); +} + +test the_offsets_the_mechanism_rules_out_match_none_of_the_31_conversions { + const kf : [20]u32 = model_k(&UNITS, 1.0, CP_FIT); + // the latch alone (+0.153 mV in this file's sign), the first decision's 9.42 mV for every decision, no offset, and the measured one with the wrong sign + assert_eq(match_count(&kf, q_sm(0.0 - LATCH_FLIP_V), &S_VP, &S_VN), 0); + assert_eq(match_count(&kf, q_sm(first_off(V_BASE)), &S_VP, &S_VN), 0); + assert_eq(match_count(&kf, 0, &S_VP, &S_VN), 0); + assert_eq(match_count(&kf, q_sm(0.0 - off_late_v()), &S_VP, &S_VN), 0); +} diff --git a/specs/analog/sar_kick.t27 b/specs/analog/sar_kick.t27 new file mode 100644 index 0000000000..3fd467454c --- /dev/null +++ b/specs/analog/sar_kick.t27 @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/analog/sar_kick.t27 -- where the SAR ADC comparator's decision offset comes from: the clock edge kicks both top plates, and the N half's bottom plates are driven through one transmission gate more than the P half's +; M4e-offset of the SAR ADC milestone (#10888, sub-issue #10941, part of #10818, epic #10486). M4e (specs/analog/sar_adc.t27, #10854) matches 30 of 31 conversions of the golden deck only with two fitted +; numbers, a top-plate parasitic Cp and a comparator offset of +6.7 mV. M4b-psp (#10856) measured the golden latch itself: dynamic_comparator flips at 0 (+-0.12 uV) and +; dynamic_comparator_top at -0.153 mV, in the SAME sign convention as the converter's offset (the latch decides "above" when VDP - VDN + off > 0; off = +0.153 mV), so the latch is not +; the 6.7 mV. This file names the source, shows it with ngspice + PSP103 experiments that change one thing at a time, and carries the numbers measured on the netlist. It runs +; no solver: it is pinned data, the estimators that read it, and tests. sar_adc.t27 takes its offset from here instead of from a fit. +; - +; THE REFERENCE (every number below is ngspice-47 + the IHP SG13G2 PSP103 model of IHP-Open-PDK 21133f21, mos_tt and cap_typ, 50 C, the golden deck of uberWAVE wp2-parser/golden/top.spice, +; GPL-2.0, fetched at run time and never vendored, sha256 c5ad548a3e1a181f1b8e81e92ead2f630e14861a36b796169f4fec6b95746e23). The experiments are the decks specs/analog/decks/sar_off_*.cir: +; each is the golden top level re-wired (the deck's own subcircuits come from the run-time file SAR_GOLDEN_SUBCKTS) with ONE thing changed, named in its header. sar_off_base.cir changes +; nothing. They run either the golden sine (sar_amp 0.6, 1.1 us, the first two conversions) or a DC input of sar_dmv millivolts (sar_amp 0, 150 ns for the first decision, 480 ns for a +; whole conversion). The quantity read everywhere is D = V(VDP) - V(VDN) at the instant the comparator clock clkc crosses 0.1 V going up: before the clock edge has reached the comparator +; (D moves by less than 0.1 mV between clkc = 0.02 V and 0.2 V, and by at most 0.08 mV from 3 ns earlier for every decision from the fourth on), so it is the plates as the DAC left them. +; The decision is the bit the latch gives (the bottom plate Bit_10 settles to 0 V for "VDP above VDN"). The flip point is the D at which the decision changes; the offset is minus it. +; - +; THE RESULT. At the first decision of a conversion (all ten bottom plates idle) the golden comparator decides "above" while VDP is still 9.42 mV below VDN; at the last two decisions +; (nine and ten plates moved) 6.66 mV and 6.90 mV below. The latch itself is 0.15 mV of that. The rest, 9.3 mV at the first decision and 6.5 to 6.7 mV at the last, is the CLOCK EDGE'S +; KICKBACK read through two unequal plates: when clkc rises, the comparator's tail node falls from 0.64 V and the drains of the input pair fall from 1.5 V, and the gate-source and +; gate-drain capacitances of the 36-unit input devices (XM3, XM4) push charge into VDP and VDN. Both plates fall, by 22.7 mV (VDP) and 30.2 mV (VDN) at the instant clkc passes half of +; Vdd; the difference, 7.5 mV, is what the latch reads as "VDP above VDN by 7.5 mV" (its regeneration then needs about 2 mV more: the offset is 9.4 mV). Plates that held the charge +; stiffly would fall by 6.87 mV each and the same together. The N half falls more because its bottom plates sit behind one more transmission gate: Bit_k is driven by DAC_control's +; inverter, or held at VCM by its tgate x2, and the N half's Bit_k_n is neg_bottom_plate_logic's x11 (idle: Bit_k_n is Bit_k through x11, so VCM through two gates in series) or x15 +; behind an inverter x12 (moved), so the N plates are softer at the nanosecond scale of the kick and VDN moves more. Row d4 tests exactly that: a resistor of one tgate's size added to +; the P half's plates makes the circuit symmetric and the offset falls to -0.9 mV. +; - +; THE EXPERIMENTS (first decision, DC input, flip point = the D between the two inputs that bracket it; offset = minus the bracket's middle, +- half the bracket). The table is also in +; specs/analog/decks/sar_off_base.cir. Moved-by is against the base row, its +- the two half brackets added. Cp is the first plate step (two directions averaged) in units of the 9 um cap_cmim. +; row candidate change (deck) offset (mV) moved by (mV) dVDP / dVDN at clkc 0.75 V (mV) Cp +; 0 base: the golden circuit sar_off_base.cir +9.42 +- 0.20 -- -22.7 / -30.2 0.647 +; a tracking switches sar_off_idealsw.cir: ideal 500 ohm switches +9.19 +- 0.20 -0.23 +- 0.40 -23.0 / -30.6 0.640 +; b comparator kickback onto the plates sar_off_nokick.cir: ideal buffers between the +0.03 +- 0.15 -9.38 +- 0.35 -0.06 / -0.10 0.621 +; plates and the comparator (55 fF stands in for its gates) +; d1 N half's bottom-plate drive sar_off_nplate.cir: N plates ideal (1.5 V - P) -25.79 +- 0.50 -35.20 +- 0.70 -17.1 / +3.3 0.582 +; d2 P half's bottom-plate drive sar_off_pplate.cir: P plates ideal (1.5 V - N) +43.87 +- 0.99 +34.45 +- 1.19 +11.1 / -24.9 0.676 +; d3 both halves' bottom-plate drive sar_off_plates.cir: both ideal, symmetric +0.27 +- 0.25 -9.14 +- 0.45 -6.86 / -6.88 0.582 +; d4 the extra tgate, given to the P half sar_off_pseries.cir: 2592 ohm (a tgate's own, sar_off_ron.cir) -0.89 +- 0.25 -10.31 +- 0.45 -29.7 / -29.2 0.690 +; between each P bottom plate and its Bit_k (1300 ohm: +4.09 +- 0.25; 3900 ohm: -4.19 +- 1.00) +; c DAC settling when clkc fires base sine run (22 decisions): D read 3 ns and 10 ns earlier than clkc = 0.1 V +; under 0.1 mV (3 ns) from the fourth decision on, 0.26 mV at the second and third; 10 ns: under 0.31 mV from the fifth on +; THE TGATE (d4). sar_off_ron.cir measures the golden tgate conducting at VCM: 2592 ohm (the inverter's pmos 1880 ohm, nmos 1427 ohm at a rail). Joining each P bottom plate to its Bit_k +; node through a resistor of that size makes the P half's drive the same as the N half's (both halves' capacitors behind one resistor from the common Bit_k node, for an idle bit): the plates +; then fall by 29.7 and 29.2 mV and the offset falls from +9.42 to -0.89 +- 0.25 mV. It is nearly linear in the resistor, +9.42, +4.09, -0.89, -4.19 mV at 0, 1300, 2592, 3900 ohm, and crosses the +; latch's own +0.153 mV at 2320 ohm, 10.5 percent under the tgate's 2592: the extra transmission gate IS the asymmetry, to the accuracy of this reading (the series resistor is not +; exactly the N half's topology, and 0.9 mV is left over). The top-plate load Cp read from the first step grows with the resistor, 0.62, 0.69, 0.85, because the biggest plate settles +; slower: the gain depends on the drive too. +; Reading the table: the comparator's own kickback is the whole offset (b: 9.42 -> 0.03, a bracket that holds the latch's 0.153 mV); the tracking switches are not (a: the sample the +; switch leaves is the plates' starting point, not a decision threshold); and what turns the kick into a difference is the plates' drive (d3: with both halves driven by ideal sources the two +; plates fall by the same 6.87 mV and the offset is the latch's again, 0.27 +- 0.25 mV, a bracket that holds 0.153). With only one half ideal the kick is lopsided the other way and the offset +; is -25.8 mV (N ideal) or +43.9 mV (P ideal): the real circuit's +9.4 is the small difference between two soft plates, one softer than the other. DAC settling (c) moves the late decisions +; by under 0.1 mV: it is not the offset. +; The same on the late decisions, from the golden sine's first two conversions in each deck (decisions from the seventh on): the offset is 6.78 mV in the base deck (the mean of the last two +; decisions below), 6.95 +- 0.42 mV with ideal switches, +0.09 +- 0.60 mV with the kickback removed and -0.10 +- 0.31 mV with ideal plates. +; - +; THE LATE DECISIONS. The converter's codes are decided by the last decisions, and the offset there is not the first decision's. 26 DC conversions of the base deck (sar_amp 0, 480 ns, +; sar_dmv = 128.54 128.84 129.14 129.44 129.64 129.74 129.84 130.04 130.34 -195.91 -196.21 -196.51 -196.81 -196.95 -197.15 -197.35 -197.55 -197.75 -244.70 -245.70 -246.70 -247.70 275.71 +; 276.71 277.71 278.71 mV, each chosen with the ideal converter model so that the decision at index 7, 8, 9 or 10 sees D within a few mV of its flip) and the sine's two conversions give 28 +; decisions at each of the indices 7 .. 10. The flip is the threshold with the fewest decisions on the wrong side (late_thr below): decision 10 -6.895 mV +- 5 uV (one decision, on a +; different bit path, is outvoted), decision 9 -6.658 mV +- 0.043, decision 8 -7.095 mV +- 0.50, decision 7 -6.975 mV +- 0.50: offsets 6.90, 6.66, 7.10 and 6.98 mV. They agree within their +; brackets; the two narrow ones differ by 0.24 mV, and one threshold for both leaves three decisions on the wrong side (a test), so the offset depends on the decision at the 0.2 mV level +; (and on the bit path: decision 10's threshold lies between -7.06 and -6.85 mV). The model takes the mean of the two narrow ones, 6.776 mV (off_late_v), for every decision. +; - +; WHAT IS NOT DONE. The kick is explained (d4 identifies the extra tgate by its measured resistance) and not derived: it is measured. A circuit-level model of the plates' drive (the transmission gates' and inverters' on-resistances from M3b's current tables, +; the DAC's capacitors, a charge pulse from the comparator's gate capacitances from M3c's charge tables, solved by M1's linear step) would give dVDP and dVDN, and the six first-decision rows +; above are its targets; that is the follow-up, #10942. The top-plate parasitic Cp stays a fit in sar_adc.t27: the table's last column shows why it is not predicted here, the +; DAC's first step depends on the drive too, 0.58 to 0.85 units. +; - +; WHAT A PASS SAYS. The six first-decision flip brackets, the 22 decisions of the golden sine's first two conversions, the 26 DC conversions that bracket the late decisions, the three other +; decks' sine brackets and the kick numbers are pinned as read from the runs; the estimators give the offsets above from them; the offset follows the kick (its sign, its size, its +; disappearance when the plates are made symmetric or the comparator is isolated) and not the switch or the DAC's settling; a changed bracket, a pooled threshold or the latch alone is caught. +; WHAT IT DOES NOT SAY. Nothing about speed, power, noise, mismatch, another corner or another temperature. Nothing about the decks beyond the numbers pinned here: the decks are data and +; nothing in this file runs ngspice. The flip points are read at 0.1 V of clkc; the comparator's own decision instant is later. The late-decision brackets are from one deck, one sine and +; 26 DC inputs; the offset at the decisions from the fourth to the sixth is not bracketed (it follows the kick, which is alike from the sixth on within 1.8 mV, a test) and the first four +; are never near a threshold in the 31 conversions. The one-half-ideal rows are rough (their brackets are 1 to 2 mV wide). The wrapper decks' conversion 0 code is 1007 where the golden +; deck's own run (pinned in sar_adc.t27) gives 1006: that conversion's last decision sits 0.006 mV from its flip (D = -6.889 mV, flip -6.895 mV, in the pinned records), so a 0.01 mV +; difference between two simulations of the same circuit decides it. +; ASCII only, English only. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module AnalogSarKick; + +use analog::linear::{fabs64}; + +pub const KIND : str = "analog-cell"; +pub const ID : str = "analog/sar_kick"; +pub const REFERENCE : str = "uberWAVE wp2-parser/golden/top.spice (read at run time, GPL-2.0, not vendored); specs/analog/decks/sar_off_*.cir"; + +; --- the six experiments (first decision) ------------------------------------------------------------------------------------------ +pub const NV : usize = 6; +pub const V_BASE : usize = 0; +pub const V_IDEALSW : usize = 1; +pub const V_NOKICK : usize = 2; +pub const V_PLATES : usize = 3; +pub const V_NPLATE : usize = 4; +pub const V_PPLATE : usize = 5; +/// The latch's own flip point with the NAND and the two inverters on its outputs, VDP - VDN in volts (M4b-psp, #10856: dp wins at -2540 units, dn at -2570 of 2^-24 V). +pub const LATCH_FLIP_V : f64 = -0.000153; + +; --- BEGIN PINNED DATA (ngspice-47 + PSP103, the decks specs/analog/decks/sar_off_*.cir, read 2026-10-19) ----------------------------- +/// The largest D1 (V) at which the first decision came out 0 (VDN above), per experiment: the flip point is above it. +pub const FLIP_LO : [6]f64 = [-0.0096150, -0.0093860, -0.0001810, -0.0005220, 0.0252870, -0.0448630]; +/// The smallest D1 (V) at which it came out 1 (VDP above): the flip point is below it. +pub const FLIP_HI : [6]f64 = [-0.0092160, -0.0089850, 0.0001180, -0.0000220, 0.0262860, -0.0428750]; +/// dVDP (V) from clkc = 0.1 V to clkc = 0.75 V at the input just below the flip. +pub const KICK_P : [6]f64 = [-0.0226940, -0.0230270, -0.0000640, -0.0068630, -0.0171100, 0.0110680]; +/// dVDN (V), the same. +pub const KICK_N : [6]f64 = [-0.0301530, -0.0305600, -0.0001020, -0.0068760, 0.0033440, -0.0248710]; +/// The change of D (V) from the first clkc = 0.1 V to the second, when the first decision was 0 (P plate raised by the biggest bit): the step of Bit_10. +pub const STEP_UP : [6]f64 = [0.7372650, 0.7373620, 0.7378990, 0.7373310, 0.7372860, 0.7372660]; +/// The same magnitude when it was 1 (P plate lowered). +pub const STEP_DN : [6]f64 = [0.7344160, 0.7346230, 0.7349780, 0.7372960, 0.7373510, 0.7330860]; +/// The tgate and the inverter on-resistances (ohm) the golden subcircuits have at the plates' bias, sar_off_ron.cir: a tgate conducting at VCM, the inverter's pmos and nmos at a rail. +pub const RON_TG : f64 = 2592.173; +pub const RON_INVP : f64 = 1880.425; +pub const RON_INVN : f64 = 1427.147; +; --- the P half's bottom plates behind a resistor (sar_off_pseries.cir, the first decision): the series resistance, the bracket of D1 (the largest D1 that gave 0, the smallest that gave 1), the kick --- +pub const NRS : usize = 3; +pub const RS_OHM : [3]f64 = [1300.0, 2592.0, 3900.0]; +pub const RS_LO : [3]f64 = [-0.0043350, 0.0006450, 0.0031970]; +pub const RS_HI : [3]f64 = [-0.0038370, 0.0011440, 0.0051900]; +pub const RS_KICK_P : [3]f64 = [-0.0264350, -0.0297480, -0.0326900]; +pub const RS_KICK_N : [3]f64 = [-0.0296700, -0.0292430, -0.0289480]; +pub const RS_STEP_UP : [3]f64 = [0.7369840, 0.7351390, 0.7312310]; +pub const RS_STEP_DN : [3]f64 = [0.7358230, 0.7345900, 0.7313190]; +; --- END PINNED DATA --------------------------------------------------------------------------------------------------------------- + +/// The middle of an experiment's flip bracket, V. +pub fn flip_mid(v: usize) -> f64 { + return (FLIP_LO[v] + FLIP_HI[v]) / 2.0; +} + +/// Half the bracket's width, V. +pub fn flip_half(v: usize) -> f64 { + return (FLIP_HI[v] - FLIP_LO[v]) / 2.0; +} + +/// The offset of an experiment's first decision: the comparator decides "above" when VDP - VDN + off > 0, so off = minus the flip point, V. +pub fn first_off(v: usize) -> f64 { + return 0.0 - flip_mid(v); +} + +/// What the clock edge did to VDP - VDN by clkc = 0.75 V (V): dVDP - dVDN. +pub fn kick_diff(v: usize) -> f64 { + return KICK_P[v] - KICK_N[v]; +} + +/// The DAC's first plate step, the two directions averaged, V. +pub fn step_mean(v: usize) -> f64 { + return (STEP_UP[v] + STEP_DN[v]) / 2.0; +} + +/// The top-plate parasitic, in units of the 9 um cap_cmim, that gives that step with M4a's ratio: 1.5 V 512 / (1023 + 32 Cp). +pub fn cp_of_step(g: f64) -> f64 { + return (768.0 / g - 1023.0) / 32.0; +} + +/// The offset with the P half's plates behind the resistor RS_OHM[i], V. +pub fn rs_off(i: usize) -> f64 { + return 0.0 - (RS_LO[i] + RS_HI[i]) / 2.0; +} + +/// The series resistance (ohm) at which the offset falls to the latch's own, interpolated between the two points that bracket it (the base row is 0 ohm); minus 1 if none does. +pub fn rs_zero_ohm() -> f64 { + const lat : f64 = 0.0 - LATCH_FLIP_V; + var ra : f64 = 0.0; + var oa : f64 = first_off(V_BASE); + var i : usize = 0; + while (i < NRS) { + const ob : f64 = rs_off(i); + if (oa > lat && ob <= lat) { + return ra + (oa - lat) / (oa - ob) * (RS_OHM[i] - ra); + } + ra = RS_OHM[i]; + oa = ob; + i = i + 1; + } + return 0.0 - 1.0; +} + +; --- the late decisions: the golden sine's first two conversions and DC conversions, decisions 7 .. 10 ----------------------------------- +; --- BEGIN PINNED DATA: the late decisions (the base deck: the sine's conversions 0 and 1, then 26 DC conversions of sar_off_base.cir, 480 ns each; 4 records per run) --- +pub const NLD : usize = 112; +/// The decision index (0 = the first, 10 = the last) of each pinned late decision. +pub const LD_IDX : [112]u32 = [ + 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, + 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, 9, 10, 7, 8, + 9, 10, 7, 8, 9, 10, 7, 8, 9, 10 +]; +/// D = V(VDP) - V(VDN) (V) at clkc = 0.1 V going up before that decision. +pub const LD_D : [112]f64 = [ + 0.0031930, -0.0025608, -0.0054462, -0.0068894, -0.0039703, -0.0097376, -0.0068565, -0.0054184, -0.0018347, -0.0075901, -0.0047023, -0.0061443, -0.0008393, -0.0065992, + -0.0094827, -0.0080309, 0.0001673, -0.0055923, -0.0084771, -0.0070296, 0.0011676, -0.0046023, -0.0074817, -0.0060379, 0.0019318, -0.0038191, -0.0067000, -0.0052636, + 0.0021326, -0.0036204, -0.0064984, -0.0079394, 0.0023324, -0.0034208, -0.0063063, -0.0077487, 0.0025360, -0.0032272, -0.0061024, -0.0075409, 0.0027141, -0.0030391, + -0.0059152, -0.0073403, 0.0028756, -0.0028836, -0.0057614, -0.0072029, 0.0031788, -0.0025785, -0.0054710, -0.0069043, 0.0034687, -0.0022855, -0.0051747, -0.0066183, + 0.0037712, -0.0019898, -0.0048688, -0.0063050, -0.0181138, -0.0123530, -0.0094739, -0.0080455, -0.0178117, -0.0120507, -0.0091872, -0.0077573, -0.0175121, -0.0117498, + -0.0088822, -0.0074425, -0.0172189, -0.0114575, -0.0085921, -0.0071466, -0.0170135, -0.0112602, -0.0083788, -0.0069476, -0.0169086, -0.0111680, -0.0082811, -0.0068464, + -0.0168103, -0.0110559, -0.0081783, -0.0067465, -0.0166324, -0.0108639, -0.0079901, -0.0065514, -0.0163319, -0.0105647, -0.0076884, -0.0062543, -0.0094787, -0.0037071, + -0.0066103, -0.0080523, -0.0084764, -0.0027060, -0.0056094, -0.0070613, -0.0074737, -0.0017199, -0.0046198, -0.0060616, -0.0064764, -0.0122691, -0.0093739, -0.0079365 +]; +/// The decision: 1 = VDP above VDN (the latch's dp won), 0 = VDN above. +pub const LD_BIT : [112]u32 = [ + 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0 +]; +; --- the 22 decisions of the base deck's sine run (conversions 0 and 1): the plates before the clock edge, how far they were from settled, and what the edge did ------ +pub const NDEC : usize = 22; +pub const DEC_IDX : [22]u32 = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +]; +/// D (V) at clkc = 0.1 V going up. +pub const DEC_D : [22]f64 = [ + -0.0314693, 0.7057833, 0.3369109, 0.1528864, 0.0607675, 0.0147064, -0.0083213, 0.0031930, -0.0025608, -0.0054462, -0.0068894, -0.2689142, 0.4681702, 0.0993561, + -0.0846738, 0.0075663, -0.0385527, -0.0154916, -0.0039703, -0.0097376, -0.0068565, -0.0054184 +]; +/// D at 3 ns earlier minus D (V): the DAC still settling. +pub const DEC_BACK3 : [22]f64 = [ + 0.0000866, -0.0000961, -0.0002548, 0.0000164, 0.0000573, 0.0000662, 0.0000744, 0.0000797, 0.0000745, 0.0000752, 0.0000745, 0.0001455, -0.0000919, -0.0002317, + 0.0000206, 0.0000929, 0.0000612, 0.0000833, 0.0000703, 0.0000685, 0.0000685, 0.0000681 +]; +/// D at 10 ns earlier minus D (V). +pub const DEC_BACK10 : [22]f64 = [ + -0.0001461, -0.0017848, -0.0027419, -0.0008008, -0.0002983, -0.0002029, -0.0001878, -0.0000516, -0.0001268, -0.0001260, -0.0001297, 0.0001869, -0.0018769, -0.0027275, + -0.0008883, 0.0001458, -0.0002288, 0.0000687, -0.0001067, -0.0001208, -0.0001313, -0.0001371 +]; +/// D at clkc = 0.75 V minus D at clkc = 0.1 V (V): the kick the edge had made by half of Vdd. +pub const DEC_KICK : [22]f64 = [ + 0.0090901, -0.0259235, -0.0188595, -0.0074434, -0.0006292, 0.0033602, 0.0056325, 0.0044781, 0.0050288, 0.0053341, 0.0054807, 0.0262881, -0.0205928, -0.0028831, + 0.0129354, 0.0039685, 0.0085284, 0.0062632, 0.0050997, 0.0056769, 0.0053932, 0.0052479 +]; +; --- the sine run of the three other decks (conversions 0 and 1), the decisions from index 6 on: the largest D that came out 0 and the smallest that came out 1 (V) --- +/// Rows: ideal switches, no kickback, ideal plates. +pub const NAT_LO : [3]f64 = [-0.0073639, -0.0006857, -0.0002141]; +pub const NAT_HI : [3]f64 = [-0.0065265, 0.0005093, 0.0004126]; +; --- END PINNED DATA --------------------------------------------------------------------------------------------------------------------- + + +/// How many pinned decisions are at index `idx`. +pub fn late_count(idx: u32) -> u32 { + var n : u32 = 0; + var i : usize = 0; + while (i < NLD) { + if (LD_IDX[i] == idx) { n = n + 1; } + i = i + 1; + } + return n; +} + +; --- the late decisions: one threshold per decision index, from the decisions on its two sides ---------------------------------------------- +/// Decisions at index `idx` that a latch with the threshold t would have decided the other way: a 0 above t, or a 1 at or below t. +pub fn late_errors(idx: u32, t: f64) -> u32 { + var n : u32 = 0; + var i : usize = 0; + while (i < NLD) { + if (LD_IDX[i] == idx) { + if (LD_BIT[i] == 0 && LD_D[i] > t) { n = n + 1; } + if (LD_BIT[i] == 1 && LD_D[i] <= t) { n = n + 1; } + } + i = i + 1; + } + return n; +} + +/// The search grid for a threshold: from -9 mV in steps of 5 uV, 1000 points. +pub const SCAN_LO : f64 = -0.009; +pub const SCAN_STEP : f64 = 0.000005; +pub const SCAN_N : usize = 1000; + +/// A threshold: the middle and the half width of the first run of grid points with the fewest errors, and that fewest number of errors. +pub struct KickThr { mid: f64, half: f64, errors: u32 } + +/// The threshold (VDP - VDN at which the latch flips, V) at decision `idx`: the grid points with the fewest disagreeing decisions, the first contiguous run of them. +pub fn late_thr(idx: u32) -> KickThr { + var best : u32 = 1000000; + var j : usize = 0; + while (j < SCAN_N) { + const e : u32 = late_errors(idx, SCAN_LO + SCAN_STEP * (j as f64)); + if (e < best) { best = e; } + j = j + 1; + } + var first : usize = SCAN_N; + var last : usize = 0; + var done : bool = false; + j = 0; + while (j < SCAN_N) { + const e : u32 = late_errors(idx, SCAN_LO + SCAN_STEP * (j as f64)); + if (e == best && !done) { + if (first == SCAN_N) { first = j; } + last = j; + } + if (e != best && first != SCAN_N) { done = true; } + j = j + 1; + } + const mid : f64 = SCAN_LO + SCAN_STEP * (((first + last) as f64) / 2.0); + const half : f64 = SCAN_STEP * (((last - first) as f64) / 2.0); + return KickThr { mid: mid, half: half, errors: best }; +} + +/// The fewest disagreeing decisions one threshold can make for decisions 9 and 10 together. +pub fn pooled_min_errors() -> u32 { + var best : u32 = 1000000; + var j : usize = 0; + while (j < SCAN_N) { + const t : f64 = SCAN_LO + SCAN_STEP * (j as f64); + const e : u32 = late_errors(9, t) + late_errors(10, t); + if (e < best) { best = e; } + j = j + 1; + } + return best; +} + +/// The late offset the converter model uses, V: minus the mean of the thresholds of the two last decisions, the two the DC conversions bracket to 0.1 mV or better. The decision +/// before them (8, 7, ...) are bracketed to +-0.5 mV and agree with it; the first decision is 2.6 mV further out and cannot change a code of the 31 (sar_adc.t27 tests it). +pub fn off_late_v() -> f64 { + return 0.0 - (late_thr(9).mid + late_thr(10).mid) / 2.0; +} + +; --- the tests ------------------------------------------------------------------------------------------------------------------------------ + +test the_six_first_decision_flips_are_bracketed_and_the_golden_circuit_flips_9_4_mv_below_zero { + var v : usize = 0; + while (v < NV) { + assert(FLIP_LO[v] < FLIP_HI[v]); + assert(flip_half(v) < 0.0011); + v = v + 1; + } + // the four rows that need a precise answer are bracketed to a quarter of a millivolt or less + assert(flip_half(V_BASE) < 0.00026); + assert(flip_half(V_IDEALSW) < 0.00026); + assert(flip_half(V_NOKICK) < 0.00026); + assert(flip_half(V_PLATES) < 0.00026); + assert fabs64(first_off(V_BASE) - 0.0094155) < 0.000000001; + assert fabs64(flip_half(V_BASE) - 0.0001995) < 0.000000001; + // the latch is 0.153 mV of it: the base offset is more than 60 times the latch's + assert(first_off(V_BASE) > 60.0 * (0.0 - LATCH_FLIP_V)); + // the latch alone cannot be the offset: the base bracket excludes the latch's flip point by more than 9 mV + assert(!(FLIP_LO[V_BASE] < LATCH_FLIP_V && LATCH_FLIP_V < FLIP_HI[V_BASE])); + assert(LATCH_FLIP_V - FLIP_HI[V_BASE] > 0.009); +} + +test ideal_switches_do_not_move_the_offset_and_removing_the_comparator_kickback_removes_it { + // (a) the two brackets overlap, and the middles are 0.23 mV apart + assert(FLIP_LO[V_IDEALSW] < FLIP_HI[V_BASE]); + assert(FLIP_LO[V_BASE] < FLIP_HI[V_IDEALSW]); + assert(fabs64(first_off(V_IDEALSW) - first_off(V_BASE)) < 0.0003); + // (b) the kickback removed: the offset drops by more than 9 mV, to a bracket that holds the latch's own flip point + assert(first_off(V_BASE) - first_off(V_NOKICK) > 0.009); + assert(FLIP_LO[V_NOKICK] < LATCH_FLIP_V); + assert(LATCH_FLIP_V < FLIP_HI[V_NOKICK]); + // and the plates no longer move at the clock edge but by the 0.06 and 0.10 mV the other loads give + assert(fabs64(KICK_P[V_NOKICK]) < 0.0001); + assert(fabs64(KICK_N[V_NOKICK]) < 0.00011); + assert(fabs64(KICK_P[V_BASE]) > 200.0 * fabs64(KICK_P[V_NOKICK])); +} + +test with_both_halfs_bottom_plates_driven_ideally_the_plates_fall_alike_and_the_offset_is_the_latchs { + // both plates fall by 6.87 mV, the same to 0.02 mV: the charge the comparator's input devices push into a stiff top plate + assert(fabs64(KICK_P[V_PLATES] - KICK_N[V_PLATES]) < 0.00002); + assert(fabs64(KICK_P[V_PLATES] + 0.00687) < 0.00001); + assert(kick_diff(V_PLATES) < 0.00002); + assert(FLIP_LO[V_PLATES] < LATCH_FLIP_V); + assert(LATCH_FLIP_V < FLIP_HI[V_PLATES]); + assert(first_off(V_BASE) - first_off(V_PLATES) > 0.009); + // the golden plates fall 3.3 (VDP) and 4.4 (VDN) times as far as stiff ones: they are soft at the nanosecond scale of the kick, and the N half is the softer + const rp : f64 = KICK_P[V_BASE] / KICK_P[V_PLATES]; + const rn : f64 = KICK_N[V_BASE] / KICK_N[V_PLATES]; + assert(rp > 3.2); + assert(rp < 3.4); + assert(rn > 4.3); + assert(rn < 4.5); + assert(rn > rp); + const r : f64 = KICK_N[V_BASE] / KICK_P[V_BASE]; + assert(r > 1.30); + assert(r < 1.36); +} + +test the_offset_follows_the_kick_in_sign_and_size_in_all_six_experiments { + var v : usize = 0; + while (v < NV) { + const kd : f64 = kick_diff(v); + const o : f64 = first_off(v); + if (fabs64(kd) > 0.001) { + assert((kd > 0.0 && o > 0.0) || (kd < 0.0 && o < 0.0)); + // the offset is larger than the kick by 1.7 to 8 mV: the latch needs a margin after the half-Vdd instant + assert(fabs64(o) > fabs64(kd)); + assert(fabs64(o) < fabs64(kd) + 0.008); + } else { + assert(fabs64(o) < 0.0006); + } + v = v + 1; + } + assert(kick_diff(V_BASE) > 0.0074); + assert(kick_diff(V_BASE) < 0.0075); + // the order of the offsets is the order of the kicks: P ideal > golden > symmetric > N ideal + assert(first_off(V_PPLATE) > first_off(V_BASE)); + assert(first_off(V_BASE) > first_off(V_PLATES)); + assert(first_off(V_PLATES) > first_off(V_NPLATE)); + assert(first_off(V_PPLATE) > 0.04); + assert(first_off(V_NPLATE) < 0.0 - 0.025); + // the swing between the two one-half-ideal rows, 69 mV, is the lopsidedness the golden circuit's 9.4 mV is a small part of + assert(first_off(V_PPLATE) - first_off(V_NPLATE) > 0.069); +} + +test the_dac_step_depends_on_the_drive_so_the_top_plate_parasitic_is_not_predicted_here { + // the first plate step, two directions averaged, as a parasitic Cp in units of the 9 um cap_cmim: 0.58 .. 0.68, and 0.647 in the golden circuit + assert fabs64(cp_of_step(step_mean(V_BASE)) - 0.647) < 0.001; + assert fabs64(cp_of_step(step_mean(V_IDEALSW)) - 0.640) < 0.001; + assert fabs64(cp_of_step(step_mean(V_PLATES)) - 0.582) < 0.001; + var lo : f64 = 1000.0; + var hi : f64 = 0.0 - 1000.0; + var v : usize = 0; + while (v < NV) { + const c : f64 = cp_of_step(step_mean(v)); + if (c < lo) { lo = c; } + if (c > hi) { hi = c; } + v = v + 1; + } + assert(hi - lo > 0.09); + // with ideal bottom plates the step is symmetric to 0.04 mV; the golden plates make it 2.85 mV different for a raised and a lowered P plate (the biggest plate has not settled) + assert(fabs64(STEP_UP[V_PLATES] - STEP_DN[V_PLATES]) < 0.00005); + assert(STEP_UP[V_BASE] - STEP_DN[V_BASE] > 0.0028); + assert(STEP_UP[V_BASE] - STEP_DN[V_BASE] < 0.0029); +} + +test the_last_two_decisions_are_bracketed_by_dc_conversions_and_their_offset_is_6_7_to_6_9_mv { + const t10 : KickThr = late_thr(10); + const t9 : KickThr = late_thr(9); + // index 9: no decision disagrees with the threshold; it is -6.6575 mV, bracketed to 0.05 mV + assert_eq(t9.errors, 0); + assert fabs64(t9.mid + 0.0066575) < 0.000001; + assert(t9.half < 0.00005); + // index 10: one decision (a different bit path, -7.061 mV) disagrees; the rest bracket -6.895 mV to 5 uV + assert_eq(t10.errors, 1); + assert fabs64(t10.mid + 0.006895) < 0.000001; + assert(t10.half < 0.00001); + assert(t10.mid < t9.mid); + // the model's late offset is their mean + assert fabs64(off_late_v() - 0.0067762) < 0.0000001; + assert(off_late_v() > 0.0 - t9.mid - 0.0002); + assert(off_late_v() < 0.0 - t10.mid + 0.0002); + // 44 times the latch's own 0.153 mV, 2.6 mV under the first decision's + assert(off_late_v() > 40.0 * (0.0 - LATCH_FLIP_V)); + const gap : f64 = first_off(V_BASE) - off_late_v(); + assert(gap > 0.0026); + assert(gap < 0.0028); + // decisions 8 and 7: the brackets are +-0.5 mV and hold the same offset to within them + const t8 : KickThr = late_thr(8); + const t7 : KickThr = late_thr(7); + assert_eq(t8.errors, 0); + assert_eq(t7.errors, 0); + assert(t8.half < 0.0006); + assert(t7.half < 0.0006); + assert(fabs64(t8.mid - (0.0 - off_late_v())) < t8.half + 0.0004); + assert(fabs64(t7.mid - (0.0 - off_late_v())) < t7.half + 0.0004); +} + +test one_threshold_for_decisions_9_and_10_together_disagrees_with_the_decisions { + // the negative control: the offsets of the two last decisions differ by 0.24 mV, and pooling them leaves at least two decisions on the wrong side + assert_eq(pooled_min_errors(), 3); + assert_eq(late_errors(9, late_thr(10).mid), 2); + assert_eq(late_errors(10, late_thr(9).mid), 4); + // the records are what the two estimators read: 112 of them, four per run + assert_eq(late_count(7) + late_count(8) + late_count(9) + late_count(10), 112); + assert_eq(late_count(10), 28); + // a latch with only its own flip point (-0.153 mV) would disagree with the 13 decisions at index 10 that came out 1 below it + assert_eq(late_errors(10, LATCH_FLIP_V), 13); +} + +test the_sine_runs_of_the_other_decks_agree_with_the_first_decision_rows { + // the golden sine's first two conversions in each deck, decisions from index 6 on: the ideal switches leave the late offset where it was (6.945 mV against 6.775) + var v : usize = 0; + while (v < 3) { + assert(NAT_LO[v] < NAT_HI[v]); + v = v + 1; + } + const o_sw : f64 = 0.0 - (NAT_LO[0] + NAT_HI[0]) / 2.0; + assert(fabs64(o_sw - off_late_v()) < 0.0003); + assert fabs64(o_sw - 0.0069452) < 0.0000001; + // the kickback removed, and the ideal plates: brackets of +-0.6 and +-0.3 mV that hold the latch's 0.153 mV + assert(NAT_LO[1] < LATCH_FLIP_V); + assert(LATCH_FLIP_V < NAT_HI[1]); + assert(NAT_LO[2] < LATCH_FLIP_V); + assert(LATCH_FLIP_V < NAT_HI[2]); + assert(NAT_HI[1] - NAT_LO[1] < 0.0013); + assert(NAT_HI[2] - NAT_LO[2] < 0.0007); + // so on the late decisions too the offset is the kickback and nothing else: 6.8 mV to under 0.6 mV + assert(off_late_v() - (0.0 - (NAT_LO[1] + NAT_HI[1]) / 2.0) > 0.006); + assert(off_late_v() - (0.0 - (NAT_LO[2] + NAT_HI[2]) / 2.0) > 0.006); +} + +test an_extra_transmission_gate_on_the_p_half_is_what_makes_the_kick_a_difference_and_a_resistor_of_its_size_removes_the_offset { + // the golden tgate conducting at VCM is 2592 ohm, the inverter's pmos 1880 and nmos 1427 at a rail (sar_off_ron.cir) + assert fabs64(RON_TG - 2592.173) < 0.001; + assert(RON_INVP < RON_TG); + assert(RON_INVN < RON_INVP); + // the first decision's offset against the resistor between each P bottom plate and its Bit_k node: 9.42, 4.09, -0.89, -4.19 mV at 0, 1300, 2592, 3900 ohm, falling at each step + var i : usize = 0; + while (i < NRS) { + assert(RS_LO[i] < RS_HI[i]); + assert(RS_HI[i] - RS_LO[i] < 0.0021); + i = i + 1; + } + assert(first_off(V_BASE) > rs_off(0)); + assert(rs_off(0) > rs_off(1)); + assert(rs_off(1) > rs_off(2)); + assert fabs64(rs_off(0) - 0.004086) < 0.000000001; + assert fabs64(rs_off(1) + 0.0008945) < 0.000000001; + // with a tgate's resistance the plates fall alike (29.7 and 29.2 mV against the golden 22.7 and 30.2) and the offset is under 1 mV + assert fabs64(RS_KICK_P[1] - RS_KICK_N[1]) < 0.0007; + assert(KICK_P[V_BASE] - RS_KICK_P[1] > 0.006); + assert(fabs64(rs_off(1)) < 0.0011); + // the P plate's kick grows with the resistor while the N plate's stays within 1.3 mV: the extra resistance is what softens the P plate + assert(RS_KICK_P[0] < KICK_P[V_BASE]); + assert(RS_KICK_P[1] < RS_KICK_P[0]); + assert(RS_KICK_P[2] < RS_KICK_P[1]); + assert(fabs64(RS_KICK_N[0] - KICK_N[V_BASE]) < 0.0006); + assert(fabs64(RS_KICK_N[2] - KICK_N[V_BASE]) < 0.0013); + // the offset crosses the latch's own at 2320 ohm: 10.5 percent under the tgate's 2592, so the extra tgate accounts for the asymmetry to about a tenth + const r0 : f64 = rs_zero_ohm(); + assert fabs64(r0 - 2320.27) < 0.5; + assert(r0 < RON_TG); + assert(RON_TG / r0 < 1.13); + assert(r0 / RON_TG > 0.88); + // the top-plate load read from the first step grows with the resistor: 0.62, 0.69, 0.85 units + const g0 : f64 = (RS_STEP_UP[0] + RS_STEP_DN[0]) / 2.0; + const g2 : f64 = (RS_STEP_UP[2] + RS_STEP_DN[2]) / 2.0; + assert fabs64(cp_of_step(g0) - 0.622) < 0.001; + assert fabs64(cp_of_step(g2) - 0.851) < 0.001; + assert(cp_of_step(g2) - cp_of_step(g0) > 0.2); +} + +test the_dac_has_settled_where_it_matters_and_the_edge_kick_is_alike_from_the_sixth_decision_on { + var i : usize = 0; + while (i < NDEC) { + const ix : u32 = DEC_IDX[i]; + // D read 3 ns earlier differs by under 0.1 mV from the fourth decision on, by at most 0.26 mV at the second and third + if (ix >= 3) { assert(fabs64(DEC_BACK3[i]) < 0.0001); } + if (ix == 1 || ix == 2) { assert(fabs64(DEC_BACK3[i]) < 0.00026); } + // 10 ns earlier: under 0.31 mV from the fifth on; 1.8 mV and 2.7 mV for the two biggest plates after their move + if (ix >= 4) { assert(fabs64(DEC_BACK10[i]) < 0.00031); } + if (ix == 1 || ix == 2) { assert(fabs64(DEC_BACK10[i]) > 0.0017); } + // the late decisions near zero: the edge has moved D by 4.5 to 6.3 mV by half of Vdd + if (ix >= 6 && fabs64(DEC_D[i]) < 0.02) { + assert(DEC_KICK[i] > 0.0044); + assert(DEC_KICK[i] < 0.0063); + } + i = i + 1; + } + // the pinned late decisions at 7 .. 10 of the sine run are the first eight records + assert_eq(DEC_IDX[7], LD_IDX[0]); + assert_eq(DEC_IDX[10], LD_IDX[3]); + assert fabs64(DEC_D[7] - LD_D[0]) < 0.00000001; + assert fabs64(DEC_D[10] - LD_D[3]) < 0.00000001; + assert fabs64(DEC_D[18] - LD_D[4]) < 0.00000001; + assert fabs64(DEC_D[21] - LD_D[7]) < 0.00000001; +} +