diff --git a/.trinity/seals/analog_AnalogAcceptSar.json b/.trinity/seals/analog_AnalogAcceptSar.json index 0015eebfff..3c366b5bbd 100644 --- a/.trinity/seals/analog_AnalogAcceptSar.json +++ b/.trinity/seals/analog_AnalogAcceptSar.json @@ -41,6 +41,10 @@ "spec": "specs/analog/sar_comparator_q.t27", "spec_hash": "sha256:6fad103672e13c04173fe1c21f502304c5a60012549f2631330a38f78c82cf27" }, + { + "spec": "specs/analog/sar_dff_q.t27", + "spec_hash": "sha256:9d9cf178184f876d9415cdd3ff064943927f9334c47f4dc5d0d3146f18691b77" + }, { "spec": "specs/analog/sar_kick.t27", "spec_hash": "sha256:b38a9a0bd22837f7ca1d5609756c522cd791f7b0f11115b7863e04cd4e15bf10" @@ -63,23 +67,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:84ed92760777384227892c46c25dc7319f654c62194f0a1b585bcf1e5db0ccb7", - "gen_hash_rust": "sha256:bd1103eca275f48e6135941be62b57a125ecd48f8ceeac28ee391a736bf4804b", - "gen_hash_verilog": "sha256:ddeb21614714f22f8c67a9f29a6f10bc4b97e125b5174c92f9acd06fc0c8f083", - "gen_hash_zig": "sha256:8b7a83587f7a39701fbeb0569b866b0063203c6c366f797530310c48f4f952dd", + "gen_hash_c": "sha256:2e83e00a3d547fde5f6eeae241f9a59c1a75289fc016fd2624448b5f0baf2739", + "gen_hash_rust": "sha256:3f9546f32a4c3643e79f64ad30b85a667efb99ec6e90e3efc8a7403279573083", + "gen_hash_verilog": "sha256:08a8b4459e44880a6316c24c529ab4b72ad6f032e92ea4eec3e29c3c80454a1a", + "gen_hash_zig": "sha256:6a492ec7efda18bada4a9e19aaacf98819de0ffec6b9393b87673c5f309fa9e9", "module": "AnalogAcceptSar", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-20T13:50:42Z", + "sealed_at": "2026-10-20T16:14:08Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:9803ab74f3033ed0f17204c4a02b0505a5d978277dcf5250e5968e7785af6033", + "spec_hash": "sha256:82ed0f7ef55084fcd62527cedfcc7664e3d4d80976895eb95315a7073dbc96a0", "spec_path": "specs/analog/accept_sar.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 16, - "total": 16, + "passed": 17, + "total": 17, "vacuous": 0 }, "toolchain": { diff --git a/.trinity/seals/analog_AnalogSarDffQ.json b/.trinity/seals/analog_AnalogSarDffQ.json new file mode 100644 index 0000000000..8af7ea4514 --- /dev/null +++ b/.trinity/seals/analog_AnalogSarDffQ.json @@ -0,0 +1,49 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+2d3203e90", + "closure": [ + { + "spec": "specs/analog/linear.t27", + "spec_hash": "sha256:3ddef7be183aba583ed285334ee389b48a65b41335e98914514235db7c4e5d62" + }, + { + "spec": "specs/analog/mos_psp103.t27", + "spec_hash": "sha256:e04b69e5c43188a3d46a4c0949cfc727f239721de73fb46e92c8a845bbbbc98e" + }, + { + "spec": "specs/analog/mos_psp103_data.t27", + "spec_hash": "sha256:86e7c154c88a325e41ff4a154a3b766cb8d65e89e2ea82176b46ee4fc523dd74" + }, + { + "spec": "specs/analog/mos_psp103_gates.t27", + "spec_hash": "sha256:1b86ebd4bba806b972b39625d135646d7880255ab934c521a51467c92e42ee35" + }, + { + "spec": "specs/analog/mos_table.t27", + "spec_hash": "sha256:cbebd820d2284174fd88fd6e5c36fb4f0a7778c9977c9742fe215b177d623bd8" + } + ], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:f1d39ed44b7abda9aba404dd77e89716469a0be4b06891c09f8130cef927f793", + "gen_hash_rust": "sha256:ae1b39ab50661640701b5f1a8bd1e88572e35d87cb7608731a94312408e1c545", + "gen_hash_verilog": "sha256:0d7cf6fb89edaf972e986a939705b49f6a26ee26ef3cab670b2adaf527901c4f", + "gen_hash_zig": "sha256:b6c8f07d8fec8216f6f5cc160bf819295a7bdca92385492ad0949c7616072e56", + "module": "AnalogSarDffQ", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-20T16:13:36Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:9d9cf178184f876d9415cdd3ff064943927f9334c47f4dc5d0d3146f18691b77", + "spec_path": "specs/analog/sar_dff_q.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 6, + "total": 6, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/docs/analog/accept-sar-2026-10-20.md b/docs/analog/accept-sar-2026-10-20.md index 632f8bdfbd..05250d6073 100644 --- a/docs/analog/accept-sar-2026-10-20.md +++ b/docs/analog/accept-sar-2026-10-20.md @@ -1,10 +1,10 @@ # The external team's per-block checks applied to our SAR ADC blocks, 2026-10-20 -Issue #11212, milestone M5c of the analog epic #10486, night loop #11063; M5d (issue #11263) adds the transistor-level rows of the nand, nor, tgate and tracking_switch, cross-checked against ngspice; M3e (issue #11283) adds a source-to-bulk axis to the PSP103 tables, which turns the tgate's two resistances from NOT MODELLED to PASS (section 3c); M4b-q (issue #11312) gives the dynamic_comparator the devices' own charge and the body effect at 27 C, which turns its decision time from FAIL to PASS and its reset from NOT MODELLED to PASS, so the block is a PASS (section 3d); M5e (issue #11350) gives the inverter's delays into 20 fF the devices' own charge and an output node free to leave the tables' 0 to Vdd range, which turns the inverter's last row from NOT MODELLED to PASS, so that block is a PASS too (section 3e); M5f (issue #11378) runs the whole tracking switch on the PSP103 tables with the devices' own charge and leakage, which turns its hold step and its droop from NOT MODELLED to PASS, so that block is a PASS too (section 3f). English, ASCII only. +Issue #11212, milestone M5c of the analog epic #10486, night loop #11063; M5d (issue #11263) adds the transistor-level rows of the nand, nor, tgate and tracking_switch, cross-checked against ngspice; M3e (issue #11283) adds a source-to-bulk axis to the PSP103 tables, which turns the tgate's two resistances from NOT MODELLED to PASS (section 3c); M4b-q (issue #11312) gives the dynamic_comparator the devices' own charge and the body effect at 27 C, which turns its decision time from FAIL to PASS and its reset from NOT MODELLED to PASS, so the block is a PASS (section 3d); M5e (issue #11350) gives the inverter's delays into 20 fF the devices' own charge and an output node free to leave the tables' 0 to Vdd range, which turns the inverter's last row from NOT MODELLED to PASS, so that block is a PASS too (section 3e); M5f (issue #11378) runs the whole tracking switch on the PSP103 tables with the devices' own charge and leakage, which turns its hold step and its droop from NOT MODELLED to PASS, so that block is a PASS too (section 3f); M5g (issue #11400) solves the dff's Q and nQ in its four stored states on the PSP103 tables: six of the eight levels are within 1.33 uV of ngspice's and Q's 1 level, after a capture of 1 and after a set, is 12.7 and 16.3 uV under it, so the dff's levels row is produced, shown with its numbers, and stays NOT MODELLED (section 3g). English, ASCII only. The external team's report of 2026-10-16 lists, for each of the 13 blocks of the golden SAR ADC, a measured value and the limit that its check enforces (conditions: tt corner, 27 C, VDD 1.5 V, cells taken from the integrated converter's netlist). The team confirmed on 2026-10-20 that the checks and their limits may be used and published here. `specs/analog/accept_sar.t27` holds those limits as named constants, one pure check function per quantity, and one function per block that measures the quantity on OUR model by `use` of the spec that holds it. This document is the result. Every "our value" below is computed by a named function of that spec and pinned by a test; every "report" value is the report's, and is also in the spec as the positive control of its check (the report's own number must pass the report's own check). -What this document does not say: nothing about a transistor-level result for the gate-level blocks (dff, clock_gen, SAR_logic), nothing about the voltage level of any digital node but the nand's and the nor's, nothing about the delay of a gate into a load, nothing about the tgate's resistance beyond the 27 outputs at 10 mV across it, nothing about power, noise, mismatch, another corner or temperature, or speed. No model was tuned to turn a FAIL into a PASS, and none was changed to make a number come out. +What this document does not say: nothing about a transistor-level result for the gate-level blocks (dff, clock_gen, SAR_logic), nothing about the voltage level of any digital node but the nand's, the nor's and the dff's Q and nQ (two of whose eight levels are outside the bar of ngspice's), nothing about the delay of a gate into a load, nothing about the tgate's resistance beyond the 27 outputs at 10 mV across it, nothing about power, noise, mismatch, another corner or temperature, or speed. No model was tuned to turn a FAIL into a PASS, and none was changed to make a number come out. ## 1. The verdict rule @@ -30,9 +30,9 @@ Over the 13 blocks of the report (inverter, nand, nor, tgate, dff, dynamic_compa |---|---|---| | PASS | 9 | clock_gen, SAR_logic, integration, nand, nor, tgate, dynamic_comparator, inverter, tracking_switch | | FAIL | 0 | | -| NOT MODELLED | 4 | dff, DAC_control, DAC_control_top, neg_bottom_plate_logic | +| NOT MODELLED | 4 | dff (its levels row is produced and outside the bar, section 3g), DAC_control, DAC_control_top, neg_bottom_plate_logic | -Over the 30 rows: 26 PASS, 0 FAIL, 4 NOT MODELLED (a test pins both counts: `as_all_blocks`, `as_all_rows`). Before M5d the counts were 3 / 1 / 9 blocks and 16 / 1 / 13 rows; after M5d and before M3e they were 5 / 1 / 7 blocks and 19 / 1 / 10 rows; after M3e and before M4b-q 6 / 1 / 6 blocks and 21 / 1 / 8 rows; after M4b-q and before M5e 7 / 0 / 6 blocks and 23 / 0 / 7 rows; after M5e and before M5f 8 / 0 / 5 blocks and 24 / 0 / 6 rows. The M5f rows are the tracking switch's hold step and droop (both PASS, NOT MODELLED before), so the tracking_switch block, whose tracking error row already passed, is a PASS (section 3f). The M5e row is the inverter's delays into 20 fF (PASS, NOT MODELLED before), so the inverter block, whose threshold row already passed, is a PASS (section 3e). The M4b-q rows are the comparator's four, all PASS: its decision time (FAIL before), its reset (NOT MODELLED before), its sign and its end levels (PASS before, on the model without charge; now on the model with it). The M5d PASS rows are the nand's and the nor's levels (2) and the tgate's off leakage (1); the M3e PASS rows are the tgate's two resistances (2), read on tables with a source-to-bulk axis (section 3c), so the tgate block is a PASS. One of the four NOT MODELLED blocks has measured, passing rows (dff 2); three have none (DAC_control, DAC_control_top, neg_bottom_plate_logic). The PASS of clock_gen and SAR_logic is the PASS of the gate-level model named in section 4, that of integration the PASS of a behavioural converter fitted to the golden deck, that of the nand, the nor and the tgate the PASS of a DC solve on the PSP103 tables, each nand and nor level within 3 uV of ngspice's and the tgate's Ron within 0.1 percent of ngspice's real cell, and that of the dynamic_comparator the PASS of a transient of the eight-node latch on the PSP103 tables with the devices' charge, its decision time 1.75 percent from ngspice's and its levels within 0.5 mV (section 3d), and that of the inverter the PASS of a transient of the cell into 20 fF on the PSP103 tables with the devices' charge, each delay 0.5 percent from ngspice's (section 3e), and that of the tracking_switch the PASS of a transient of its ten transistors and the inverter on the PSP103 tables with the devices' charge, the hold step 2.4 and 9.4 percent from ngspice's and the droop inside the bound of ngspice's run (section 3f). +Over the 30 rows: 26 PASS, 0 FAIL, 4 NOT MODELLED (a test pins both counts: `as_all_blocks`, `as_all_rows`). Before M5d the counts were 3 / 1 / 9 blocks and 16 / 1 / 13 rows; after M5d and before M3e they were 5 / 1 / 7 blocks and 19 / 1 / 10 rows; after M3e and before M4b-q 6 / 1 / 6 blocks and 21 / 1 / 8 rows; after M4b-q and before M5e 7 / 0 / 6 blocks and 23 / 0 / 7 rows; after M5e and before M5f 8 / 0 / 5 blocks and 24 / 0 / 6 rows. M5g (the dff's levels) changes no count: the row is produced, two of its eight levels are outside the bar of ngspice's, and it stays NOT MODELLED, so the dff block does too (section 3g; the same 9 / 0 / 4 blocks and 26 / 0 / 4 rows as after M5f). The M5f rows are the tracking switch's hold step and droop (both PASS, NOT MODELLED before), so the tracking_switch block, whose tracking error row already passed, is a PASS (section 3f). The M5e row is the inverter's delays into 20 fF (PASS, NOT MODELLED before), so the inverter block, whose threshold row already passed, is a PASS (section 3e). The M4b-q rows are the comparator's four, all PASS: its decision time (FAIL before), its reset (NOT MODELLED before), its sign and its end levels (PASS before, on the model without charge; now on the model with it). The M5d PASS rows are the nand's and the nor's levels (2) and the tgate's off leakage (1); the M3e PASS rows are the tgate's two resistances (2), read on tables with a source-to-bulk axis (section 3c), so the tgate block is a PASS. One of the four NOT MODELLED blocks has measured, passing rows (dff 2); three have none (DAC_control, DAC_control_top, neg_bottom_plate_logic). The PASS of clock_gen and SAR_logic is the PASS of the gate-level model named in section 4, that of integration the PASS of a behavioural converter fitted to the golden deck, that of the nand, the nor and the tgate the PASS of a DC solve on the PSP103 tables, each nand and nor level within 3 uV of ngspice's and the tgate's Ron within 0.1 percent of ngspice's real cell, and that of the dynamic_comparator the PASS of a transient of the eight-node latch on the PSP103 tables with the devices' charge, its decision time 1.75 percent from ngspice's and its levels within 0.5 mV (section 3d), and that of the inverter the PASS of a transient of the cell into 20 fF on the PSP103 tables with the devices' charge, each delay 0.5 percent from ngspice's (section 3e), and that of the tracking_switch the PASS of a transient of its ten transistors and the inverter on the PSP103 tables with the devices' charge, the hold step 2.4 and 9.4 percent from ngspice's and the droop inside the bound of ngspice's run (section 3f). The report's corners smoke suite (SAR inverter delay 62 to 124 ps, comparator decision 467 to 829 ps over 5 corners at -40, 27 and 125 C) is a test run, not a block, and is not in the 13. It is NOT MODELLED (last row of the table). @@ -52,7 +52,7 @@ Times in ps or ns as written. "Report" is the report's measured value; "limit" i | tgate | worst Ron over 0.1 to 1.4 V (27 points) | 2777.5 ohm at 1.0 V with the body effect; ngspice's real cell 2776.1 ohm at 1.0 V, +0.049 % | 2.78 kohm | <= 50 kohm | PASS (within 10 % of ngspice) | `gt_tg_worst` | | tgate | off leakage (full swing) | 291.0 pA; ngspice 285.6 pA at the output source, 271.4 pA at the input source | 0.29 nA | <= 1 nA | PASS (within 7.2 % of ngspice) | `gt_tg_off` | | dff | edge capture, hold, set, reset (logic level, 11 stages) | all right | all right | exact | PASS | `as_sar_run`, `as_dff_set_reset` | -| dff | levels of the 8 observation points | not produced | all right | +-50 mV | NOT MODELLED | | +| dff | levels of the 8 observation points (Q and nQ, four stored states; M5g) | worst 1.848 uV from a rail; against ngspice: six within 1.33 uV, Q's 1 level 12.73 and 16.30 uV under it | all right | +-50 mV, and each within 10 uV of ngspice's | NOT MODELLED (inside the limit, two outside the bar; section 3g) | `dq_levels`, `as_row_dff_levels` | | dff | clock-to-Q | 546 ps | 524 ps | 1 to 3000 ps | PASS | `as_sar_run` | | dynamic_comparator | sign at +-2 mV, 0.75 V common mode | +2 mV: dp wins; -2 mV: dn wins | right | right | PASS | `as_cmp` | | dynamic_comparator | levels after the decision (2 ns) | winner 1.49933 V, loser 2.26 mV; ngspice 1.49884 V, 2.283 mV (0.49 and 0.02 mV) | right | +-50 mV | PASS (within 5 mV of ngspice) | `as_cmp` | @@ -261,12 +261,49 @@ THE NEGATIVE CONTROLS (the bar is 10 percent of ngspice's step, 1 uV of its droo What this does not say: the body effect on the charge, the junctions of the drain and source (their charge is in the tables with the bulk on the source; their leakage and their voltage-dependent capacitance are in no table, and ngspice's droop includes them), the gate leakage, PSP103's NQS and gate-resistance effects, a changing input, another clock slew, corner, temperature or load, noise, mismatch, power, speed, hardware. The droop is an upper bound at the resolution of one IUNIT, not a measurement below 1.164 pA. The model is f64 with integer table reads: the test-side model of the cell, not the board's arithmetic. +## 3g. The dff's levels on the PSP103 tables (M5g, issue #11400) + +The dff block's levels row (the eight observation points, Q and nQ after a reset, a capture of 1, a capture of 0 and a set; limit +-50 mV from the rail) was NOT MODELLED: the gate-level model of `sar_logic.t27` has no voltages. `specs/analog/sar_dff_q.t27` solves the cell's nodes on the tt 27 C PSP103 log tables, the ones of the nand, the nor and the tgate (`mos_psp103_gates.t27`), and compares Q and nQ with ngspice-47's run of the same cell against the same bar as the gates: 10 uV. + +THE CELL AND THE REFERENCE. The golden dff has ten cells: four transmission gates, four nors and two inverters (the clock's and nQ's), each of the devices of the gates (NMOS 1.0 um, PMOS 3.0 um, L 0.45 um). The reference is `specs/analog/decks/accept_dff.cir` run in ngspice-47 with the PSP103 (IHP-Open-PDK 21133f21, mos_tt, 27 C), the golden deck's own dff subcircuit included at run time (a file that is not vendored here), no load on Q or nQ. Reset is high to 10 ns; the clock is high 20 to 30 ns and 50 to 60 ns; D is 1.5 V to 40 ns and then 0; Set is high 70 to 80 ns. The four states are read at 15 ns (reset), 45 ns (capture 1), 68 ns (capture 0) and 95 ns (set), 4.8 to 14.8 ns after an edge, with the clock, Reset and Set low. The deck prints each level as its distance from the rail, to 6 digits. With the clock low the master's tgate x1 and the slave's keeper x7 conduct (as in `sar_logic.t27`) and x3 and x9 are off, so there are three circuits and not four: the stored 0 with D at 1.5 V (the reset), the stored 0 with D at 0 V (the capture of 0) and the stored 1 with D at 0 V (the capture of 1 and the set, one circuit). + +THE MODEL. In a stored state every node sits microvolts from a rail, so each gate sees its inputs at a rail and a node is the DC level of the gate that drives it: `gt_nor` for the four nors, `dc_out` of `mos_psp103.t27` for the inverter, an on tgate passes its input; all reused through `use`, not copied. One load is in the tables and enters: the off x3 has net2 on one side and net3 on the other, on opposite rails when D opposes the stored bit (the reset and the stored 1), and leaks `gt_tg_off` (291.0 pA) from the high side to the low side, through the on devices of x2 and through x7 and x5; that moves net2, net3 and net4 off their rails by 0.4 to 1.6 uV and changes neither Q nor nQ at the word (59.6 nV). The off x9 has both sides on one rail in every state and leaks less than half an IUNIT. A sweep over the nodes is at its fixed point at once (a test pins one and six sweeps). + +THE NUMBERS (uV, the distance from the rail; gap = ours minus ngspice's): + +| | reset | capture 1 | capture 0 | set | +|---|---|---|---|---| +| Q, ours | 0.0596 | 1.8477 | 0.0596 | 1.8477 | +| Q, ngspice-47 | 1.3901 | 14.5814 | 1.3901 | 18.1517 | +| Q, gap | -1.3305 | -12.7337 | -1.3305 | -16.3040 | +| nQ, ours | 0.2980 | 0.1192 | 0.2980 | 0.1192 | +| nQ, ngspice-47 | 0.7207 | 0.6355 | 0.7207 | 0.6378 | +| nQ, gap | -0.4227 | -0.5163 | -0.4227 | -0.5186 | + +The report's check, every level within 50 mV of its rail: ours worst 1.848 uV, ngspice's worst 18.152 uV (27000 and 2750 times inside). The cross-check, each of ours within 10 uV of ngspice's: SIX OF THE EIGHT ARE (the largest gap of the six is 1.33 uV) AND TWO ARE NOT, Q's 1 level after a capture of 1 (12.73 uV under ngspice's, 1.27 times the bar) and after a set (16.30 uV under, 1.63 times the bar). The row is therefore not produced by the rule of section 1 and is NOT MODELLED, with these numbers beside it; it is not a FAIL, since both numbers are inside the report's limit by a wide margin. The reset and the capture of 0 are one circuit as far as Q and nQ go, and ngspice agrees with itself there to 0.04 nV; the capture of 1 and the set are one circuit, ours is one answer for both, and ngspice's two reads of it are 3.57 uV apart in Q (14.58 and 18.15 uV) and 2.3 nV in nQ: no DC solve can be at the same time at both reads, and the cause of the spread is not measured here (two candidates, neither tested: the floating middle nodes of the series stacks have time constants of tens of ns, and the deck reads 4.8 to 14.8 ns after an edge). + +WHY, and what is not shown. All eight gaps are negative, for a 1 node and for a 0 node: ours is below ngspice's everywhere. That is the sign of a DC current at the node that the tables do not carry, a load that draws a 1 node down from its rail and a source that lifts a 0 node off its. The tables are drain currents Id(Vgs, Vds); the gate current is in none of them (the gate current that leaves through the drain at Vds = 0 and Vgs above 1.2 V, at most 2 units = 0.6 nA, is held at 0 in `mos_psp103_data.t27`, the table being unsigned). Q is the gate node of four devices, an NMOS and a PMOS of x5 and of x6, so the current is there. Read through the table's own on-resistance at the node (3582.5 ohm for the two PMOS in series that hold a 1, 1301.5 ohm for the NMOS that holds a 0), the gap is a current of 3.55 nA (capture 1) and 4.55 nA (set) at Q's 1 level, 1.02 nA at Q's 0 level, 0.24 nA and 0.40 nA at nQ's 1 and 0 levels; the leakage of the off tgate that is in the tables, 291 pA, is 12 and 16 times smaller. That a gate current of that size is the whole of it is NOT shown: no number for it is in the repository, and the deck prints nothing that would separate it from another cause. The nand and nor levels of `mos_psp103_gates.t27` have the same sign against the same ngspice (every one of ours is below its, by 0.14 to 2.36 uV, in cells with no gate to drive), consistent with a gate current in the cell's own devices and not a proof of one. What would settle it is a deck that prints the DC gate current of one NMOS and one PMOS at 1.5 V and a table of it (a signed one: the tables are unsigned); it is not in the repository. + +THE NEGATIVE CONTROLS (Q at a 1 is the level the controls move; the bar is 10 uV of ngspice's): + +| the break | Q's 1 level | gap to ngspice's (worst of the eight) | verdict | +|---|---|---|---| +| none (the cell as the tables give it) | 1.848 uV | 16.304 uV | NOT MODELLED (two levels outside the bar) | +| x4's pull-up dead (a zero table in the two PMOS) | 1.5 V from its rail | 1.5 V | refused by the report's 50 mV and by the bar | +| the NMOS table dead | Q's 0 level at 1.5 V, Q's 1 level 1.4 mV off its rail | 1.5 V | refused by the report's 50 mV and by the bar | +| x4's pull-up at a hundredth of its width | 186.38 uV | 171.80 uV (17 times the bar) | inside the report's 50 mV, refused by the bar | +| x4's pull-up at a TENTH of its width | 18.656 uV (10.1 times ours) | 4.07 uV: all eight inside the bar | NOT refused: the row would be a PASS | + +THE LIMIT OF THE CONTROL, and why no PASS is claimed. The issue asks for the pull-up of x4 at a tenth of its width to be refused. It is not: on these tables it is accepted. A pull-up ten times weaker lifts Q's 1 level from 1.85 uV to 18.66 uV, which lands within 4.07 uV and 0.50 uV of ngspice's 14.58 and 18.15 uV, because ngspice's level contains a load current worth 13 to 16 uV that the tables lack; so the 10 uV bar passes the wrong cell and refuses the right one. The bar is not loosened to make the row a PASS, and no PASS is claimed for the dff's levels: a PASS here would be as likely a wrong cell as a right one. The same test (`the_dff_levels_are_solved_on_the_psp103_tables...` in `accept_sar.t27`) pins that `as_row_dff_levels` gives PASS for the tenth-width cell and NOT MODELLED for the real one, so that nobody reads the row as more than it is. The width itself is seen by the model: Q's 1 level scales as one over the pull-up's width (10.1 times for a tenth, 101 times for a hundredth); it is the reference that is off. + +What this does not say: that two of the eight levels agree with ngspice's within the bar (they are 12.7 and 16.3 uV under it); that the gate current is the cause; the levels of net1 to net5 (derived, no ngspice number: net2 is 0.4 to 2.9 uV off its rail, net3 0.8 to 3.4 uV, net4 0.4 to 2.9 uV, net5 0.06 to 1.85 uV); another corner or temperature; a load on Q or nQ; the dff with its clock high, or a level in a transition; the clock-to-Q (that row is the gate-level model's). The model is a table model of the PSP103 without the gate current, not the PSP103. + ## 4. What each model is, and what that means for the verdict - inverter. The PSP103 tables of the SG13G2 NMOS (W 1.0 um) and PMOS (W 3.0 um), tt, 27 C, current in the log domain (`mos_psp103.t27`, M3c-2). The threshold is the input at which the DC transfer falls through Vdd / 2: 0.689243 V on the log tables, 0.689252 V on the linear ones; ngspice's trip point is 0.689 V and the report's 0.69 V. The delays into 20 fF (M5e, `mos_psp103_inv20.t27`, section 3e) are the same two devices (W 1.0 um and 3.0 um, L 0.45 um, m 1) with the devices' own drain and source charge, an ideal gate step, the output node free to leave the tables' 0 to Vdd range, backward Euler of 1 ps: fall 75.755 ps and rise 93.246 ps, +0.51 and +0.50 percent from ngspice's converged run of the same cell (75.368 and 92.778 ps), inside the report's 1 to 500 ps: the row PASSES, so the block does. Before M5e they were NOT MODELLED: the transient was validated at 500 fF (tpLH 1.344 ns, tpHL 1.018 ns) and 10 pF only, and those 500 fF delays are not compared with the 1 to 500 ps limit, they are a different load. - nand, nor. Two rows each. The truth table is at gate level (`sar_logic.t27`: a gate is a delay and a truth table, a value is 0, 1 or X and never a voltage). The levels in volts are the DC solution of the two-input gate on the same PSP103 log tables as the inverter (`mos_psp103_gates.t27`, M5d): the nand is two NMOS in series against two PMOS in parallel, the nor the same seen from the supply, each device the W and L of the golden netlist's (NMOS 1.0 um, PMOS 3.0 um, L 0.45 um), so no new table was made. The solver is the inverter's bisection with the stack's middle node solved inside it (a second bisection); the currents are read by the integer log-domain reader in steps of 1.164 pA and the output has a resolution of 59.6 nV. Every level of both gates is within 3 uV of ngspice's run of the same cell, and microvolts from its rail. - tgate. The same tables and the same reader (`mos_psp103_gates.t27`): conducting, the NMOS reads (Vdd - Vout, 10 mV) and the PMOS (Vin, 10 mV), and R is 10 mV over the sum, at 27 outputs from 0.10 V to 1.40 V (the measurement of `sar_off_ron.cir`); off, both devices sit at Vgs = 0, Vds = 1.5 V and the leakage is the sum of their two table words. The leakage is a row, and so are the two resistances since M3e: the NMOS bulk is on ground and the PMOS bulk on the supply, so while the gate conducts the NMOS source (the output) is Vout above its bulk and the PMOS source (the input, Vout + 10 mV) is 1.5 V - (Vout + 10 mV) below its; each device is read on the tables at that source-to-bulk voltage (`gt_tg_i` with body true, section 3c). Without the axis (body false) the 2-D tables are made with source and bulk on one rail, and ngspice's real cell is 30 and 24 percent above the model (section 3b); with each bulk tied to its own source ngspice and the 2-D model agree to 0.06 percent, which is how the cause was found. The 2592 ohm of a golden tgate in `sar_kick.t27` is a different measurement (50 C, the golden deck's own bias) and is not compared. -- dff. Gate level (`sar_logic.t27`): capture, hold, set, reset and the clock-to-Q delay are measured on it; the levels in volts are NOT MODELLED. The dff clock-to-Q is the first stage of the sequencer, Valid rise to CK11 rise with D = VDD, 546 ps (ngspice's own is 550 ps). +- dff. Gate level (`sar_logic.t27`): capture, hold, set, reset and the clock-to-Q delay are measured on it. The levels in volts (M5g, section 3g, `sar_dff_q.t27`) are a DC solve of the cell's nodes on the tt 27 C PSP103 log tables, in the three circuits the deck's four stored states make: Q and nQ sit within 1.85 uV of a rail, six of the eight within 1.33 uV of ngspice's, Q's 1 level 12.73 and 16.30 uV under it because the tables hold no gate current; the row is NOT MODELLED and so is the block. The dff clock-to-Q is the first stage of the sequencer, Valid rise to CK11 rise with D = VDD, 546 ps (ngspice's own is 550 ps). - dynamic_comparator. The model of `sar_comparator_q.t27` (section 3d): the eight-node latch of M4b on the PSP103 log tables at 27 C, read on the source-to-bulk axis, with the devices' own charge (M3c tables, the source terminal closed by its two edges), backward Euler of 1.6 ps. All four rows are produced and PASS: the sign (dp wins at +2 mV, dn at -2 mV), the end levels at 2 ns (1.49933 V and 2.26 mV, 0.49 and 0.02 mV from ngspice's), the reset levels 1.4 ns after the clock falls (3.08 and -0.17 mV, 0.19 and 0.08 mV from ngspice's) and the decision time, 613.07 ps, 1.75 percent over ngspice's 602.54 ps on the golden comparator at 27 C and inside the report's 10 to 10000 ps. Before it, the model was the no-charge latch on the 50 C tables of `sar_comparator_psp.t27`, whose decision came 98 times too fast (5.35 ps against 523 ps at +2 mV, within 6.5 percent of ngspice with the charge swamped, the comparison its tables were built for) and FAILED the 10 ps lower limit; that FAIL was the model's, and the model with the charge removed still fails (the negative control of section 3d). - tracking_switch. A SQUARE-LAW bootstrapped switch (`sar_switch.t27`, MOSFET level 1). The tracking error at DC is measured: 1.97 uV, which is the fixed-point arithmetic floor (ngspice's own on the square-law deck is 0.03 to 0.05 uV), and it is a row because ngspice's PSP103 run of the same cell at the same conditions (`accept_switch.cir`) gives +0.29 and -0.17 uV, 2.3 uV from it. The hold step and the droop were NOT MODELLED on that model, because the square law has no gate capacitance, so no charge injection, and no leakage: it returns a step of 0.18 uV and a droop of exactly 0, which sit far inside +-30 mV and +-5 mV by having no effect at all, and that is not a measurement of the effect the report's -1.6 and -1.7 mV show. Since M5f the two rows are measured on a second model, the PSP103 switch of `sar_switch_q.t27` (section 3f): the whole cell, ten transistors and the inverter, on the tt 27 C tables with the devices' own charge and the source-to-bulk axis, backward Euler of 10 ps, at the report's conditions. The block has three rows and PASSES. - clock_gen, SAR_logic. Gate level with delay tables measured on the golden gates in ngspice (`sar_logic.t27`, 113 gates, the delay cell with its history); the delays are the model's own run, within 8 percent of ngspice's for every clkc edge. The slow delay is 16.145 ns (ngspice 16.147 ns), the fast one 9.822 ns (ngspice 9.669 ns, 1.6 percent), the ratio 1.644 (the report's 1.64). The stop time is taken from the eleventh Valid rise to the last clkc fall at the golden pace (40 ns): 5.704 ns (ngspice 5.807 ns); the report's 10.7 ns is not reproduced by this definition and the report's definition is not in the material this was made from, so the two are not compared. SAR_logic is driven by eleven Valid pulses of 10 ns every 20 ns, so CK11 to CK1 spans ten periods plus the 126 ps by which CK1's load makes it later than CK11. @@ -295,6 +332,7 @@ The four numbers agree within a few LSB. Without the measured offset the end cod - Integration: see above. The report's sample points are not in the material this was made from. - The gate-level rows run on delays measured in ngspice at tt, 50 C (the golden deck's temperature), not the report's 27 C; the square-law switch has no temperature. The M5d rows are at tt, 27 C, the report's temperature. - nand and nor: the levels are the DC solution with no load, which is the level a gate settles to into 10 fF; the report's measurement is a settled level into 10 fF, and no transient is run here. +- dff levels: the DC solution with no load on Q or nQ, in the four stored states of `accept_dff.cir`, each read 4.8 to 14.8 ns after an edge in ngspice's transient (not a `.op`); the report's own states and timing are not in the material this was made from. - tgate: Ron is 10 mV over the current with 10 mV across the gate (the measurement of `sar_off_ron.cir`, at 27 C here), on 27 outputs 50 mV apart from 0.10 V to 1.40 V; the report's sweep grid and its definition of Ron are not in the material this was made from, and the reference run (ngspice, same definition, same grid) gives the report's 2.44 and 2.78 kohm to 0.8 and 0.14 percent. The leakage is at 1.5 V across the off gate, the bias at which ngspice gives the report's 0.29 nA. ## 6. Negative controls @@ -322,6 +360,8 @@ Every check function accepts the report's own value and refuses a broken one, so | clock ratio | the trim code ignored | 1.0, refused by 1.2 | | SAR_logic counts and order | the fifth dff with Set and Reset exchanged | 2, 5, 11 high instead of 1, 4, 11, wrong order, refused | | dff set and reset | the same dff | the set row loses CK7, the clks reset sets it, refused | +| dff levels (M5g) | x4's pull-up dead / the NMOS table dead / x4's pull-up at a hundredth of its width | 1.5 V from a rail / 1.5 V / Q's 1 level 186.4 uV, 171.8 uV from ngspice's: refused by the report's 50 mV or by the 10 uV bar (the row stays NOT MODELLED) | +| dff levels (M5g), THE LIMIT | x4's pull-up at a tenth of its width | Q's 1 level 18.656 uV, within 4.07 uV of ngspice's: all eight inside the bar, NOT refused (the row would be a PASS for the wrong cell; section 3g) | | integration error | the MSB capacitor halved | 237 LSB, refused by 64 | | nand, nor truth table | the other gate's table, one row flipped | refused | | nand levels | the PMOS table zeroed / the NMOS table zeroed | the 1 levels fall to under 0.4 mV, 1.5 V off / the 0 level rises to 1.5 V; refused by the report's check and by the cross-check | @@ -339,7 +379,8 @@ A limit of the control: some of the report's limits are wide, and a model error ## 7. Not done, and why -- The dff's levels and the three DAC blocks are NOT MODELLED (the tgate's two resistances were too, until M3e gave the tables a source-to-bulk axis, the comparator's reset until M4b-q ran a second clock phase, the inverter's 20 fF delays until M5e freed the output node from the tables' range, and the tracking switch's hold step and droop until M5f ran the whole cell on the PSP103 tables). Each needs a model that does not exist: circuit-level plate drivers and latches, and the dff at transistor level. They are not approximated here. +- The dff's levels are produced (M5g) and two of the eight are outside the bar of ngspice's, so the row is NOT MODELLED: Q's 1 level after a capture of 1 and after a set is 12.7 and 16.3 uV under ngspice's, with a current of 3.6 and 4.6 nA at the node that the tables do not carry (Q drives four gates; the tables have no gate current). What would close it is a signed table of the DC gate current of the NMOS and the PMOS (a deck that prints it is not in the repository), and ngspice's own two reads of the one stored state (capture 1 and set, 3.57 uV apart) would be settled by a longer hold before the read or an `.op`; neither is done. The control the issue names (the pull-up at a tenth of its width refused) is not met, section 3g. +- The three DAC blocks are NOT MODELLED (the tgate's two resistances were too, until M3e gave the tables a source-to-bulk axis, the comparator's reset until M4b-q ran a second clock phase, the inverter's 20 fF delays until M5e freed the output node from the tables' range, and the tracking switch's hold step and droop until M5f ran the whole cell on the PSP103 tables). Each needs a model that does not exist: circuit-level plate drivers and latches. They are not approximated here. - The tracking switch's hold step at 1.2 V into 2 pF is 9.4 percent over ngspice's (0.6 point inside the bar) and at the DAC's 1.3 V it is 10.0 percent over: the gap grows with the input, and the body effect on the CHARGE of the switch's channel (the charge tables have the bulk tied to the source) is the candidate, not isolated (section 3f). The step also depends on the length of the track phase through net5's slow creep, which rests on the tables' subthreshold currents. - The droop is the table's upper bound at gate drive 0, to the resolution of one IUNIT (1.164 pA, 0.24 uV over 413 ns): not a measurement below it, and the drain and source junctions' leakage is in no table. - The nand's, the nor's and the tgate's delays into a load, and their levels in a transient: M5d does the DC levels only. @@ -358,10 +399,12 @@ A limit of the control: some of the report's limits are wide, and a model error ./target/release/t27b test specs/analog/sar_comparator_q.t27 --check ./target/release/t27c test-report specs/analog/sar_switch_q.t27 ./target/release/t27b test specs/analog/sar_switch_q.t27 --check +./target/release/t27c test-report specs/analog/sar_dff_q.t27 +./target/release/t27b test specs/analog/sar_dff_q.t27 --check ./target/release/t27b test specs/analog/mos_psp103_gates.t27 --check ./target/release/t27b test specs/analog/mos_psp103_vsb_data.t27 --check ``` -`accept_sar.t27` has 16 tests (the verdict rule, one for each block or group of blocks, the reference run against the report, the hold step and the droop of M5f with their two negative controls, the corners line and the counts of blocks and of rows); `mos_psp103_inv20.t27` has 10 (the two delays against ngspice, the halving of the step, the waveform and the kick, the three negative controls, the source-to-bulk read, the premise of the bisection, the continuity across the exchange of the channel's ends, the 500 fF anchor, the rest levels); `mos_psp103_gates.t27` has 9 (the nand, the nor, the gates' negative controls, the tgate without the body effect, the real cell against the 2-D read, the leakage, and three of M3e: the Ron with the body effect, the held-out currents between slices, the controls); `mos_psp103_vsb_data.t27` has 3 (the pins and the layout of the 12 slices, the fall of the current with Vsb, the read at Vsb = 0 against the 2-D read); `mos_table.t27` has 21 (the one of M3e: the Vsb read on two synthetic slices). `sar_comparator_q.t27` has 8 (the units, the closure and the extensions of the charge, the Jacobian, the decision time at +-2 mV, the input sweep and the free start, the long run with the levels and the reset, the negative controls, the reference data). `sar_switch_q.t27` has 8 (the netlist against the deck, the rest and track equilibria with net5's creep, the hold step at the report's two conditions and at the DAC's, the step independence of the time step, the track phase's overdrive, the charge removed and the body axis removed, the droop and ten times it). Every number in sections 3, 3b, 3c, 3d, 3e and 3f is pinned in them. `t27b test --check` exits 0 with no jit/interp mismatch. +`accept_sar.t27` has 17 tests (the verdict rule, one for each block or group of blocks, the reference run against the report, the hold step and the droop of M5f with their two negative controls, the dff's levels row of M5g with its controls and its limit, the corners line and the counts of blocks and of rows); `sar_dff_q.t27` has 6 (the nodes in the three stored circuits, the one answer of each circuit and the off x9, the eight levels against ngspice, the sign of the gaps and the current they imply, ngspice's own two reads, the controls and the limit); `mos_psp103_inv20.t27` has 10 (the two delays against ngspice, the halving of the step, the waveform and the kick, the three negative controls, the source-to-bulk read, the premise of the bisection, the continuity across the exchange of the channel's ends, the 500 fF anchor, the rest levels); `mos_psp103_gates.t27` has 9 (the nand, the nor, the gates' negative controls, the tgate without the body effect, the real cell against the 2-D read, the leakage, and three of M3e: the Ron with the body effect, the held-out currents between slices, the controls); `mos_psp103_vsb_data.t27` has 3 (the pins and the layout of the 12 slices, the fall of the current with Vsb, the read at Vsb = 0 against the 2-D read); `mos_table.t27` has 21 (the one of M3e: the Vsb read on two synthetic slices). `sar_comparator_q.t27` has 8 (the units, the closure and the extensions of the charge, the Jacobian, the decision time at +-2 mV, the input sweep and the free start, the long run with the levels and the reset, the negative controls, the reference data). `sar_switch_q.t27` has 8 (the netlist against the deck, the rest and track equilibria with net5's creep, the hold step at the report's two conditions and at the DAC's, the step independence of the time step, the track phase's overdrive, the charge removed and the body axis removed, the droop and ten times it). Every number in sections 3, 3b, 3c, 3d, 3e, 3f and 3g is pinned in them. `t27b test --check` exits 0 with no jit/interp mismatch. -The ngspice numbers are the printed output of three decks, none changed after printing: `IHP_PDK= IHP_OSDI= ngspice -b specs/analog/decks/accept_gates.cir` (the eight gate levels and the off leakage), `accept_tgate.cir` (the conducting tgate, the real cell and the cell with its bulks on its sources, 27 points) and `accept_switch.cir` (the PSP103 tracking switch, four copies: tracking error, hold step, droop); ngspice-47 with the PSP103 of IHP-Open-PDK 21133f21, mos_tt, 27 C. M4b-q adds four: `sar_cmp_q_ref.cir` (the 27 C comparator: the settled start, the golden deck's start, the node rows and the reset), `sar_cmp_q_ref50.cir` (the proof that the written-out netlist is the golden comparator), `sar_cmp_q_free.cir` (the free start) and `sar_cmp_q_be.cir` (ngspice's backward Euler on a fixed grid of 0.2 to 1.6 ps), each run once on the reference host in 51 to 53 seconds and none reading the golden deck. M5e adds `psp103_inv20_fine.cir` (the inverter into 20 fF with an ideal gate step: the two 50 percent crossings at three time steps, the rest levels, the kick and 20 samples after each edge), run on the reference host in 47 seconds (an earlier revision of the deck, without the 10-digit print and the two further steps, gave the same numbers; the container and its files were removed after each run). M3e adds `psp103_idpa_vsb_tt.cir` (the log-domain current tables of both devices at seven source-to-bulk voltages and 1000 held-out currents between them, section 3c). The decks contain the W, L and m of the cells (facts of the circuit) and none of the golden netlist's text. +The ngspice numbers are the printed output of three decks, none changed after printing: `IHP_PDK= IHP_OSDI= ngspice -b specs/analog/decks/accept_gates.cir` (the eight gate levels and the off leakage), `accept_tgate.cir` (the conducting tgate, the real cell and the cell with its bulks on its sources, 27 points) and `accept_switch.cir` (the PSP103 tracking switch, four copies: tracking error, hold step, droop); ngspice-47 with the PSP103 of IHP-Open-PDK 21133f21, mos_tt, 27 C. M4b-q adds four: `sar_cmp_q_ref.cir` (the 27 C comparator: the settled start, the golden deck's start, the node rows and the reset), `sar_cmp_q_ref50.cir` (the proof that the written-out netlist is the golden comparator), `sar_cmp_q_free.cir` (the free start) and `sar_cmp_q_be.cir` (ngspice's backward Euler on a fixed grid of 0.2 to 1.6 ps), each run once on the reference host in 51 to 53 seconds and none reading the golden deck. M5e adds `psp103_inv20_fine.cir` (the inverter into 20 fF with an ideal gate step: the two 50 percent crossings at three time steps, the rest levels, the kick and 20 samples after each edge), run on the reference host in 47 seconds (an earlier revision of the deck, without the 10-digit print and the two further steps, gave the same numbers; the container and its files were removed after each run). M3e adds `psp103_idpa_vsb_tt.cir` (the log-domain current tables of both devices at seven source-to-bulk voltages and 1000 held-out currents between them, section 3c). M5g adds `accept_dff.cir` (the golden dff at transistor level, no load: Q and nQ in four stored states, section 3g), run once on the reference host with the golden deck's dff subcircuit included at run time from a file that is not vendored here. The decks contain the W, L and m of the cells (facts of the circuit) and none of the golden netlist's text. diff --git a/specs/analog/accept_sar.t27 b/specs/analog/accept_sar.t27 index 6db00e6c83..130e79ae61 100644 --- a/specs/analog/accept_sar.t27 +++ b/specs/analog/accept_sar.t27 @@ -4,7 +4,9 @@ ; two resistances from NOT MODELLED to PASS; M4b-q (issue #11312) gives the dynamic_comparator the devices' own charge and the body effect at 27 C (sar_comparator_q.t27), which turns its decision time from FAIL to PASS and ; its reset from NOT MODELLED to PASS; M5e (issue #11350) gives the inverter's delays into 20 fF the devices' own charge and an output node free to leave the tables' 0 to Vdd range ; (mos_psp103_inv20.t27), which turns the last row of the inverter from NOT MODELLED to PASS; M5f (issue #11378) runs the whole tracking switch on the PSP103 tables with the devices' charge -; (sar_switch_q.t27), which turns its hold step and its droop from NOT MODELLED to PASS. The +; (sar_switch_q.t27), which turns its hold step and its droop from NOT MODELLED to PASS; M5g (issue #11400) solves the dff's Q and nQ in its four stored states on the same tables (sar_dff_q.t27): six of the +; eight levels are within 1.4 uV of ngspice's and two (Q at a 1, after a capture of 1 and after a set) are 12.7 and 16.3 uV under it, past the 10 uV bar, so the dff's levels row is produced, shown with its numbers, +; and stays NOT MODELLED. The ; external team's report of 2026-10-16 lists, for the 13 blocks of the golden SAR ADC, a ; measured value and the limit that its check enforces (conditions: tt corner, 27 C, VDD 1.5 V, cells taken from the integrated converter's netlist). The team ; confirmed on 2026-10-20 that the checks and their limits may be used and published in this repository. This file holds those limits as named constants, one @@ -38,7 +40,12 @@ ; has with the bulks on the rails): 2461.2 ohm and 2777.5 ohm, within 0.07 and 0.05 percent of ngspice's real cell (2459.6 and 2776.1 ohm; the largest of 27 gaps is 0.09 ; percent), inside the report's 30 and 50 kohm: all three rows PASS and the block PASSes. Before M3e the tables had no body effect and read 1718 and 2109 ohm, 30 and 24 ; percent under the real cell, past the 10 percent bar, and the two rows were NOT MODELLED; that read is kept (body false) as the negative control and still gives NOT MODELLED. -; dff the gate level (inside SAR_logic): capture, hold, set, reset and the clock-to-Q delay are measured on it; the levels in volts are NOT MODELLED. +; dff the gate level (inside SAR_logic): capture, hold, set, reset and the clock-to-Q delay are measured on it. THE LEVELS (M5g, sar_dff_q.t27) are a DC solve of the cell's nodes on the same tt 27 C +; PSP103 log tables as the nand and the nor, in the four stored states of the deck (accept_dff.cir): Q and nQ sit within 2 uV of a rail (the report's limit is 50 mV). A row is PASS only inside +; the report's limit AND with each of the eight within 10 uV of ngspice-47's run of the same cell. Six are, within 1.33 uV; Q's 1 level is 12.73 uV (after a capture of 1) and 16.30 uV (after a +; set) under ngspice's, because Q drives four gates and the tables hold no gate current: the two do not agree, so the row is NOT MODELLED, with those numbers beside it, and the block stays NOT +; MODELLED. NEGATIVE CONTROLS: a dead pull-up, a dead NMOS and a pull-up at a hundredth of its width are refused; THE LIMIT: a pull-up at a TENTH of its width lands within 4.07 uV of ngspice's and is +; ACCEPTED by the 10 uV bar (the row would be PASS for that wrong cell and is NOT MODELLED for the right one), so the bar is not loosened to make a PASS and none is claimed. ; dynamic_comparator the model of sar_comparator_q.t27 (M4b-q): the PSP103 log tables of the two devices at 27 C, the report's temperature, read on the source-to-bulk axis (the input pair and XM8, XM9 have ; their source off their bulk), the devices' own drain, gate and source charge from the M3c tables, backward Euler of 1.6 ps. Before it the comparator was the NO-CHARGE ; model on the 50 C tables, which decided 98 times too fast (5.35 ps against the 523 ps of ngspice's golden-deck start at +2 mV) and FAILED the 10 ps lower limit. Now all four @@ -76,7 +83,7 @@ ; code) and accepts the report's own measured value, so no check is always-FAIL or always-PASS. Where a model can be broken to leave the limit, that is done too: the ; PMOS table zeroed (the output never falls through Vdd / 2: the reader returns -0.031 V, no crossing), the inputs of the comparator exchanged (the sign flips), the comparator's charge removed (5.40 ps, refused by the 10 ps limit and by the cross-check), the boost capacitor removed (tracking error 25.4 mV, refused by the limit and by the cross-check), the trim code ; ignored (ratio 1.0), a dff with Set and Reset exchanged (the order, the counts and the set and reset rows fail), a converter whose MSB capacitor is halved (237 LSB), a gate with -; its PMOS or NMOS table zeroed (the 1 levels at 0 V, the 0 level at 1.5 V), the tgate with the NMOS table in both slots (521 pA, inside the report's 1 nA and 82 percent over ngspice's, so the cross-check refuses it), the tgate read with Vsb forced to 0 (M3e: 1718 and 2109 ohm, inside the report's limits and 30 and 24 percent under ngspice's real cell, so the cross-check refuses it and the rows are NOT MODELLED), the tracking switch with the devices' charge removed (M5f: no hold step, under 1 uV, inside the report's 30 mV and 100 percent from ngspice's, so the hold row FAILS) and with ten times the leakage (2.4 uV of droop, inside the report's 5 mV and 2.4 times the 1 uV bound of ngspice's run at 0.3 V, so the droop row FAILS). +; its PMOS or NMOS table zeroed (the 1 levels at 0 V, the 0 level at 1.5 V), the tgate with the NMOS table in both slots (521 pA, inside the report's 1 nA and 82 percent over ngspice's, so the cross-check refuses it), the tgate read with Vsb forced to 0 (M3e: 1718 and 2109 ohm, inside the report's limits and 30 and 24 percent under ngspice's real cell, so the cross-check refuses it and the rows are NOT MODELLED), the tracking switch with the devices' charge removed (M5f: no hold step, under 1 uV, inside the report's 30 mV and 100 percent from ngspice's, so the hold row FAILS) and with ten times the leakage (2.4 uV of droop, inside the report's 5 mV and 2.4 times the 1 uV bound of ngspice's run at 0.3 V, so the droop row FAILS), the dff with x4's pull-up dead, its NMOS dead or its pull-up at a hundredth of its width (M5g: 1.5 V, 1.5 V and 186 uV off Q's rail, refused by the report's 50 mV or by the 10 uV bar; the row stays NOT MODELLED), and, as a LIMIT and not a control, the same pull-up at a tenth of its width (Q's 1 level 18.7 uV, within 4.07 uV of ngspice's: inside the 10 uV bar, so the bar does not see it, sar_dff_q.t27). ; Honest limit of the control: a few of the report's limits are wide (the inverter threshold 0.5 to 1.0 V, a bridge of 2 units gives 45.5 LSB against 64), and a model ; error inside the limit is not seen by that check; the sar_adc.t27 tests see it by the codes. The level cross-check of a gate does not see a device's width (the two tables in each ; other's slots stay within 3 uV of ngspice's levels, mos_psp103_gates.t27 pins it); the tgate's leakage and Ron do. @@ -87,7 +94,8 @@ ; - ; WHAT A PASS SAYS. For the clock_gen, SAR_logic, integration, nand, nor, tgate, dynamic_comparator, inverter and tracking_switch blocks: every quantity the report's check enforces is produced by our model and is inside the report's ; limit. For a row marked PASS inside a NOT MODELLED block: that quantity, at the model's level above, is inside the limit. WHAT IT DOES NOT SAY. Nothing about a transistor-level -; result for the gate-level blocks (dff, clock_gen, SAR_logic), nothing about voltage levels of any digital node but the nand's and nor's, nothing about the delay of any gate into a load, +; result for the gate-level blocks (dff, clock_gen, SAR_logic), nothing about voltage levels of any digital node but the nand's and nor's and the dff's Q and nQ (whose two 1 levels, after a capture of 1 and after a set, are 12.7 and +; 16.3 uV under ngspice's, past the bar: the tables have no gate current), nothing about the delay of any gate into a load, ; nothing about the tgate's resistance beyond the 27 outputs at 10 mV across it (the body effect is in the tables from M3e, at 27 C), nothing about the comparator's nodes between the end levels ; (up to 83 mV off ngspice's in the regeneration, 0.31 V in the reset) or its resolution time (4.9 percent early), nothing about power, noise, mismatch or another corner or temperature (the report's smoke ; suite runs five corners at -40, 27 and 125 C; our tables have no -40 or 125 C and the line is NOT MODELLED), nothing about the body effect on the switch's charge, its junctions' leakage or a changing @@ -101,6 +109,7 @@ use analog::linear::{fabs64, q_sm}; use analog::mos_psp103::{dc_out, cross_idx, rel_gap, DC_POINTS, DC_STEP}; use analog::mos_psp103_data::{TT_LG_N, TT_LG_P, TT_N, TT_P}; use analog::mos_psp103_gates::{GT_B_LEVEL_V, GT_B_REL, GT_NG_RTG, GT_NG_LEAK_OUT_A, GT_NG_LEAK_IN_A, Lv, gt_levels, gt_tg_ron, gt_tg_v, gt_tg_worst, gt_tg_off, gt_ref_max}; +use analog::sar_dff_q::{DqLv, dq_levels, dq_scale}; use analog::mos_psp103_inv20::{IV_DT_FS, IV_M_NOCHARGE, IV_M_RANGE, NG20_TPLH_PS, NG20_TPHL_PS, IvDelays, iv_delays}; use analog::sar_comparator::{SC_NN, SC_DP, SC_DN, SC_VCM, SC_VTH_V, SC_H2, sc_build_table}; use analog::sar_comparator_q::{SQ_K, SQ_NS, SQ_NL, SQ_JF, SQ_M_NOCHARGE, SQ_M_NOBODY, SQ_BAR_LEVEL_V, NG_S_FIRST_PS, NG_NODES_TRAP, NG_RESET_TRAP, sq_run, sq_t_first, sq_ps, sq_volts, sq_sign}; @@ -131,7 +140,7 @@ pub const AS_N_BLOCKS : u32 = 13; pub const AS_N_ROWS : u32 = 30; ; --- why a row is NOT MODELLED (one reason per kind, quoted by the doc) --------------------------------------------- -pub const AS_WHY_LEVELS : str = "the model is gate level: a node is 0, 1 or X, never a voltage"; +pub const AS_WHY_LEVELS : str = "the dff's Q and nQ are solved on the PSP103 tables (M5g) and sit within 2 uV of a rail, but Q's 1 level is 12.7 and 16.3 uV under ngspice's, past the 10 uV bar: the tables hold no gate current, and Q drives four gates"; pub const AS_WHY_TG_RON : str = "a read with no body effect (Vsb forced to 0) puts ours 30 percent under ngspice's real cell at 0.75 V and 24 percent under at the worst point, past the 10 percent bar; with each bulk tied to its source ngspice agrees with ours to 0.06 percent; with the source-to-bulk axis (M3e) the rows are measured and PASS"; pub const AS_WHY_DAC_CONTROL : str = "the DAC takes its plate levels as ideal sources: the level is the input, not a result"; pub const AS_WHY_DAC_TOP : str = "the eleven latches and the plate lines are not a circuit in any spec; the converter's decision order is behavioural"; @@ -788,14 +797,21 @@ pub fn as_blk_tgate() -> Blk { return as_blk_tg(as_tgate(&TT_LG_N, &TT_LG_P, true)); } -/// dff: capture and hold (the sequencer's thermometer at each Valid edge and between edges), set and reset (the Set pulse and the clks reset), the clock-to-Q of the first -/// stage; the levels in volts are not modelled. +/// dff, the levels row (M5g, issue #11400): the row of a measured DqLv (sar_dff_q.t27: our Q and nQ in the four stored states of the deck against ngspice-47's run of the same cell). It is a row only when each of +/// the eight is within 10 uV of ngspice's (GT_B_LEVEL_V, the bar of the gates), PASS then only inside the report's 50 mV; a value that does not agree is shown with its numbers and is NOT MODELLED, on the terms +/// of the nand's and the nor's levels. On the cell as the tables give it two of the eight (Q's 1 level, 12.7 and 16.3 uV under) do not agree and the row is NOT MODELLED. +pub fn as_row_dff_levels(dl: DqLv) -> u32 { + return as_verdict(dl.gap_v <= GT_B_LEVEL_V, as_chk_dff_levels(dl.worst_v)); +} + +/// dff: capture and hold (the sequencer's thermometer at each Valid edge and between edges), set and reset (the Set pulse and the clks reset), the levels of Q and nQ in the four stored states (as_row_dff_levels, +/// on the tt 27 C tables), and the clock-to-Q of the first stage. pub fn as_blk_dff() -> Blk { var b : Blk = as_blk0(); const r : SarRun = as_sar_run(false); const s : DffSr = as_dff_set_reset(false); b = as_add(b, as_verdict(r.ok and s.ok, as_chk_dff_behaviour(r.edge_ok, r.hold_ok and s.hold_ok, s.set_ok, s.reset_ok))); - b = as_add(b, as_nm()); + b = as_add(b, as_row_dff_levels(dq_levels(&TT_LG_N, &TT_LG_P, &TT_LG_P))); b = as_add(b, as_verdict(r.ok, as_chk_dff_c2q((r.c2q_ps as f64)))); return b; } @@ -1229,11 +1245,12 @@ test the_dff_behaviour_and_clock_to_q_are_measured_at_gate_level_and_a_dff_with_ assert(!as_chk_dff_behaviour(t, f, t, t)); assert(!as_chk_dff_behaviour(t, t, f, t)); assert(!as_chk_dff_behaviour(t, t, t, f)); - // the levels: the gate-level model has none; a dff output 0.2 V off its rail is refused + // the levels check itself: an output 1 mV off its rail passes, one 0.2 V off is refused (the levels row is measured on the PSP103 tables in the next test) var rail : f64 = 0.001; var off : f64 = 0.2; assert(as_chk_dff_levels(rail)); assert(!as_chk_dff_levels(off)); + // the block: behaviour and clock-to-Q PASS, the levels row is NOT MODELLED (M5g: produced, two of its eight levels past the bar), so the block is not a pass const b : Blk = as_blk_dff(); assert_eq(b.pass, 2); assert_eq(b.fail, 0); @@ -1241,6 +1258,67 @@ test the_dff_behaviour_and_clock_to_q_are_measured_at_gate_level_and_a_dff_with_ assert_eq(as_block_verdict(b), AS_NM); } +test the_dff_levels_are_solved_on_the_psp103_tables_six_of_eight_are_within_the_bar_and_two_are_not_so_the_row_is_not_modelled_and_the_bar_does_not_see_a_pull_up_a_tenth_of_the_width { + // M5g (issue #11400), sar_dff_q.t27: Q and nQ of the golden dff in its four stored states, the DC solve on the tt 27 C log tables against ngspice-47's run of the same cell (accept_dff.cir). + // Ours: the worst distance of any of the eight from its rail is 1.848 uV (the report's limit is 50 mV: 27000 times inside); ngspice's worst is 18.152 uV (2750 times inside) + const dl : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &TT_LG_P); + assert(dl.worst_v > 0.0000018477); + assert(dl.worst_v < 0.0000018478); + assert(as_chk_dff_levels(dl.worst_v)); + // the cross-check against ngspice: six of the eight are within the bar of 10 uV (the largest gap of the six is 1.33 uV), Q's 1 level after a capture of 1 and after a set are not (12.73 and 16.30 uV under), + // so the worst gap is 16.304 uV, 1.63 times the bar + assert_eq(dl.n_in, 6); + assert(dl.gap_v > 0.0000163039); + assert(dl.gap_v < 0.0000163040); + assert(dl.gap_v > GT_B_LEVEL_V); + assert(dl.gap_v < 1.64 * GT_B_LEVEL_V); + // the verdict: a value that does not agree with ngspice is shown and is NOT MODELLED, never a PASS (the nand's and the nor's levels are the same rule), and not a FAIL: it is inside the report's limit + assert_eq(as_row_dff_levels(dl), AS_NM); + assert(as_row_dff_levels(dl) != AS_PASS); + assert(as_row_dff_levels(dl) != AS_FAIL); + // NEGATIVE CONTROLS on the model (each a table of one device zeroed or scaled; sar_dff_q.t27 has the numbers): x4's pull-up dead and the NMOS dead are 1.5 V from a rail, refused by the report's 50 mV + // and by the bar; x4's pull-up at a HUNDREDTH of its width is 186.4 uV from Q's rail, inside the 50 mV and 171.8 uV from ngspice's, refused by the bar + var dead : [4851]u32 = [0] ** 4851; + var tenth : [4851]u32 = [0] ** 4851; + var hundredth : [4851]u32 = [0] ** 4851; + dq_scale(&TT_LG_P, &tenth, 217706); + dq_scale(&TT_LG_P, &hundredth, 435412); + const d4 : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &dead); + assert(!as_chk_dff_levels(d4.worst_v)); + assert(d4.gap_v > 100000.0 * GT_B_LEVEL_V); + assert_eq(as_row_dff_levels(d4), AS_NM); + const dn : DqLv = dq_levels(&dead, &TT_LG_P, &TT_LG_P); + assert(!as_chk_dff_levels(dn.worst_v)); + assert(dn.gap_v > 100000.0 * GT_B_LEVEL_V); + assert_eq(as_row_dff_levels(dn), AS_NM); + const h : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &hundredth); + assert(as_chk_dff_levels(h.worst_v)); + assert(h.worst_v > 0.000186); + assert(h.worst_v < 0.000187); + assert(h.gap_v > 17.0 * GT_B_LEVEL_V); + assert_eq(as_row_dff_levels(h), AS_NM); + // THE LIMIT OF THE CONTROL, pinned so that nobody reads a PASS as more than it is: x4's pull-up at a TENTH of its width puts Q's 1 level at 18.656 uV, which is within 4.07 uV of ngspice's (their 14.58 and + // 18.15 uV include a load current the tables do not carry), so every one of the eight is inside the bar and the row for THAT cell is a PASS, where the cell as the tables give it is NOT MODELLED. The bar is not + // loosened and no PASS is claimed for the dff's levels + const t : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &tenth); + assert_eq(t.n_in, 8); + assert(t.gap_v > 0.0000040748); + assert(t.gap_v < 0.0000040749); + assert(t.gap_v < GT_B_LEVEL_V); + assert(t.worst_v > 0.000018656); + assert(t.worst_v < 0.000018657); + assert_eq(as_row_dff_levels(t), AS_PASS); + assert(t.gap_v < dl.gap_v); + // the row's three verdicts are reachable and each is a different number, and an unmeasured gap is never a PASS: the same worst level with a gap of 1 mV is NOT MODELLED + var gap_big : f64 = 0.001; + var worst_ok : f64 = 0.000001; + var worst_bad : f64 = 0.06; + var gap_ok : f64 = 0.000001; + assert_eq(as_row_dff_levels(DqLv { worst_v: worst_ok, gap_v: gap_big, n_in: 0 }), AS_NM); + assert_eq(as_row_dff_levels(DqLv { worst_v: worst_ok, gap_v: gap_ok, n_in: 8 }), AS_PASS); + assert_eq(as_row_dff_levels(DqLv { worst_v: worst_bad, gap_v: gap_ok, n_in: 8 }), AS_FAIL); +} + test the_comparator_rows_are_measured_on_the_27_c_charge_model_and_all_four_pass_inside_the_limits_and_within_the_bars_of_ngspice { const c : Cmp = as_cmp(0); // the sign at +-2 mV: dp wins at +2 mV and dn at -2 mV, as in ngspice's run of the same comparator @@ -1697,7 +1775,8 @@ test the_thirteen_blocks_count_9_pass_0_fail_and_4_not_modelled_and_their_30_row assert_eq(t.fail, 0); assert_eq(t.nm, 4); assert_eq(t.pass + t.fail + t.nm, AS_N_BLOCKS); - // and over the rows of those blocks (the corners line is not a block and has no row here): 30 rows, of which 26 PASS, 0 FAIL and 4 NOT MODELLED (before M5f's hold step and droop, which were NOT + // and over the rows of those blocks (the corners line is not a block and has no row here): 30 rows, of which 26 PASS, 0 FAIL and 4 NOT MODELLED (M5g's dff levels row is produced, two of its eight levels are + // outside the 10 uV bar of ngspice's, and it stays NOT MODELLED, so no count moved; before M5f's hold step and droop, which were NOT // MODELLED, 24, 0 and 6 rows, 8, 0 and 5 blocks; before M5e's inverter delays, which were NOT MODELLED, 23, 0 and 7 rows, 7, 0 and 6 blocks; before M4b-q's charge the comparator's decision time FAILED // and its reset was NOT MODELLED: 21, 1 and 8 rows, 6, 1 and 6 blocks; before M3e's source-to-bulk axis the tgate's two resistances were NOT MODELLED: 19, 1 and 10 rows, 5, 1 and 7 blocks) const r : Blk = as_all_rows(); diff --git a/specs/analog/decks/accept_dff.cir b/specs/analog/decks/accept_dff.cir new file mode 100644 index 0000000000..faad9ae530 --- /dev/null +++ b/specs/analog/decks/accept_dff.cir @@ -0,0 +1,41 @@ +IHP SG13G2 dff of the golden SAR deck at transistor level, PSP103 mos_tt 27 C: Q and nQ after reset, capture 1, capture 0 and set +* specs/analog/decks/accept_dff.cir -- reference data for the dff's levels row of specs/analog/accept_sar.t27 (M5g, #11400, epic #10486). +* The dff is the golden SAR deck's own subcircuit (the file top.spice of its parser's golden directory, GPL-2.0: fetched at run time, never vendored), included from +* SAR_GOLDEN_SUBCKTS (made at run time: sed -n '/^.subckt tracking_switch/,$p' top.spice | grep -v -i -E '^\.(end|global)([[:space:]]|$)'). +* Pins of the golden subcircuit: dff VDD VSS Q CLK nQ D Reset Set. No load on Q or nQ: the level is the DC level the cell settles to. +* Stimulus (edges 0.2 ns): Reset high to 10 ns; CLK rises at 20 ns and 50 ns (high 10 ns each); D is 1.5 V to 40 ns, then 0; +* Set high from 70 ns to 80 ns. Measured after each state has settled: reset (15 ns), capture 1 (45 ns), capture 0 (68 ns), set (95 ns). +* Prints dev__, the deviation from the rail in volts, 6 significant digits: for a 1 the voltage from VDD to the node (dq, dnq below), +* for a 0 the node itself, so the digits fall on the deviation and not on the 1.5 V. +* Paths are NOT in this file: ngspice reads IHP_PDK, IHP_OSDI and SAR_GOLDEN_SUBCKTS from the environment. +* Run: IHP_PDK= IHP_OSDI= SAR_GOLDEN_SUBCKTS= ngspice -b accept_dff.cir (ngspice-47) +.lib $IHP_PDK/ihp-sg13g2/libs.tech/ngspice/models/cornerMOSlv.lib mos_tt +.include $SAR_GOLDEN_SUBCKTS +.temp 27 +.options reltol=1e-7 vntol=1e-12 abstol=1e-15 gmin=1e-15 itl1=500 itl4=200 num_threads=2 +VDD vdd 0 1.5 +VSS vss 0 0 +VRST rst 0 PWL(0 1.5 10n 1.5 10.2n 0) +VSET set 0 PWL(0 0 70n 0 70.2n 1.5 80n 1.5 80.2n 0) +VD d 0 PWL(0 1.5 40n 1.5 40.2n 0) +VCLK clk 0 PWL(0 0 20n 0 20.2n 1.5 30n 1.5 30.2n 0 50n 0 50.2n 1.5 60n 1.5 60.2n 0) +XD vdd vss q clk nq d rst set dff +* ideal voltage-controlled sources (no load on the cell): dq = VDD - Q, dnq = VDD - nQ, the deviation of a 1 level +EDQ dq 0 vdd q 1 +EDNQ dnq 0 vdd nq 1 +.tran 5p 100n +.control +pre_osdi $IHP_OSDI/psp103.osdi +pre_osdi $IHP_OSDI/psp103_nqs.osdi +set numdgt=12 +run +meas tran dev_q_rst FIND v(q) AT=15n +meas tran dev_nq_rst FIND v(dnq) AT=15n +meas tran dev_q_c1 FIND v(dq) AT=45n +meas tran dev_nq_c1 FIND v(nq) AT=45n +meas tran dev_q_c0 FIND v(q) AT=68n +meas tran dev_nq_c0 FIND v(dnq) AT=68n +meas tran dev_q_set FIND v(dq) AT=95n +meas tran dev_nq_set FIND v(nq) AT=95n +.endc +.end diff --git a/specs/analog/sar_dff_q.t27 b/specs/analog/sar_dff_q.t27 new file mode 100644 index 0000000000..ae03163f12 --- /dev/null +++ b/specs/analog/sar_dff_q.t27 @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/analog/sar_dff_q.t27 -- the DC levels of the golden SAR deck's dff in its four stored states, on the PSP103 log tables, judged by ngspice +; M5g of the analog emulation epic (#10486, issue #11400). specs/analog/accept_sar.t27 applies the external team's per-block checks to our models; its dff block had one row it could not produce, the LEVELS of +; the eight observation points (Q and nQ after a reset, a capture of 1, a capture of 0 and a set), limit 50 mV from the rail. This file solves the cell's nodes on the same tt 27 C tables as the nand, the nor +; and the tgate (mos_psp103_gates.t27, M5d) and compares Q and nQ with ngspice-47's run of the same cell (specs/analog/decks/accept_dff.cir), against the same bar as the gates: 10 uV. Reused, not copied: the +; nor is `gt_nor`, the inverter is `dc_out` of mos_psp103.t27, the transmission gate's resistance and leakage are `gt_tg_ron` and `gt_tg_off`; what is new is the netlist, the sweep that closes its loop and +; the comparison. +; - +; THE CELL. The golden dff (pins VDD VSS Q CLK nQ D Reset Set): x10 inverts CLK; x1 passes D to net1 while CLK is LOW (the master is transparent while the clock is low, as in sar_logic.t27); x2 = nor(Set, net1) +; gives net2; x3 passes net2 to net3 while CLK is high; x4 = nor(Reset, net3) gives Q; x6 inverts Q to nQ; x5 = nor(Q, Set) gives net4; x7 passes net4 to net3 while CLK is low; x8 = nor(net2, Reset) gives net5; +; x9 passes net5 to net1 while CLK is high. The devices are those of the gates: NMOS 1.0 um, PMOS 3.0 um, L 0.45 um, tt, 27 C, the tables TT_LG_N and TT_LG_P. The deck reads each state with the clock low and +; Reset and Set low, so x1 and x7 are on and x3 and x9 are off, and D reaches net1. There are three circuits, not four: a stored 0 with D at 1.5 V (the reset: net1 = 1, net2 = 0, net3 = 1, Q = 0, nQ = 1, net4 = 1, +; net5 = 1), a stored 0 with D at 0 V (the capture of 0: net1 = 0, net2 = 1, net3 = 1, net4 = 1, net5 = 0) and a stored 1 with D at 0 V (the capture of 1 and the set, which are one circuit: net1 = 0, net2 = 1, +; net3 = 0, Q = 1, nQ = 0, net4 = 0, net5 = 0). +; THE SOLVE. In a stored state every node sits microvolts from a rail, so a gate sees its inputs at a rail and a node is the DC level of the gate that drives it: `gt_nor` for x2, x4, x5, x8 (its two inputs in the +; order the cell names them), `dc_out` for x6, and an on transmission gate passes its input (net1 is D, exactly). One load is in the tables and enters: the off x3 has net2 on one side and net3 on the other, on +; opposite rails whenever D opposes the stored bit (the reset and the stored 1), and leaks `gt_tg_off` (291.0 pA at full swing) from the high side to the low side; that current flows through x2's on devices on net2's +; side and through x7 (on, its `gt_tg_ron`) and x5's on devices on net3's side (`dq_rout`, a small-signal read at 10 mV of the on pull network: two PMOS in series for a 1, one NMOS for a 0), which moves net2, net3 and net4 +; away from their rails. The off x9 has net5 and net1 on the same rail in every state and leaks nothing (to the word, a test). A sweep over the seven nodes closes the loop net3 -> Q -> net4 -> net3; it is at its fixed point at +; once, and a test pins that one and six sweeps give the same words. Voltages are words of 2^-24 V (59.6 nV). Q and nQ are the two numbers the deck prints; net1 to net5 are derived with no ngspice number beside them and +; are context, not a row. +; - +; THE REFERENCE. specs/analog/decks/accept_dff.cir: the golden deck's own dff subcircuit (fetched at run time, never vendored), no load on Q or nQ, Reset high to 10 ns, CLK high 20 to 30 ns and 50 to 60 ns, D at +; 1.5 V to 40 ns then 0, Set high 70 to 80 ns; read at 15 ns (reset), 45 ns (capture 1), 68 ns (capture 0) and 95 ns (set), each 15 ns or less after an edge. ngspice-47, mos_tt, 27 C, the reference host. The +; deck prints each level as its distance from the rail, to 6 digits: the DQ_NG_Q and DQ_NG_NQ constants below, none changed after printing. +; - +; THE RESULT, our model against that run, in uV (the distance from the rail; ours minus ngspice's below it): +; reset capture 1 capture 0 set +; Q ours 0.0596 1.8477 0.0596 1.8477 +; ngspice 1.3901 14.5814 1.3901 18.1517 +; gap -1.3305 -12.7337 -1.3305 -16.3040 +; nQ ours 0.2980 0.1192 0.2980 0.1192 +; ngspice 0.7207 0.6355 0.7207 0.6378 +; gap -0.4227 -0.5163 -0.4227 -0.5186 +; The report's check, every level within 50 mV of its rail: ours worst 1.848 uV, ngspice's worst 18.152 uV, 27000 and 2750 times inside. The cross-check bar, 10 uV (GT_B_LEVEL_V): SIX OF THE EIGHT +; ARE INSIDE IT (the largest gap of those six is 1.33 uV) AND TWO ARE NOT: Q's 1 level after a capture of 1 (12.73 uV under ngspice's) and after a set (16.30 uV under). accept_sar.t27 therefore keeps the row +; NOT MODELLED, with these numbers beside it. +; - +; WHY, and what is not shown. All eight gaps are NEGATIVE: ours is below ngspice's, for a 1 node and for a 0 node. That is the sign of a DC current at the node that our tables do not carry: a load that draws a 1 +; node down from its rail and a source that lifts a 0 node off its. The tables are drain currents Id(Vgs, Vds); the gate current is not in them (mos_psp103_data.t27: the gate current that leaves through the drain +; at Vds = 0 and Vgs above 1.2 V, at most 2 units = 0.6 nA, is held at 0, the table being unsigned). Q is the gate node of four devices (x5 and x6, an NMOS and a PMOS each) and nQ and net4 are their drains, so the +; current is there: read through the table's own on-resistance at the node (`dq_rout`: 3582.5 ohm for the two PMOS in series that hold a 1, 1301.5 ohm for the NMOS that holds a 0), the gap is a current of 3.55 nA (capture 1) and +; 4.55 nA (set) at Q's 1 level, 1.02 nA at Q's 0 level, 0.24 nA and 0.40 nA at nQ's 1 and 0 levels. That a gate current of that size is the whole of it is NOT shown here: no number for it is in the repository, and +; the deck prints nothing that would separate it from another cause. The gates of mos_psp103_gates.t27 show the same sign against the same ngspice (every nand and nor level of ours is below its, by 0.14 to 2.36 uV, in cells +; with no gate to drive), which is consistent with a gate current in the cell's own devices and does not prove one; the dff adds the gates Q drives. The deck's own two reads of ONE stored state differ: capture 1 and set are the same circuit (stored 1, D 0 V, Reset and Set low, clock +; low) and ngspice gives 14.58 and 18.15 uV for Q, 3.57 uV apart, where reset and capture 0 (the stored 0) agree to 0.04 nV; a DC solve has one answer for the pair, ours, so the two reads cannot both be the +; DC level, and the cause of the spread is not measured here (two candidates, neither tested: the series stacks' middle nodes float, with time constants of tens of ns, and the deck reads 4.8 to 14.8 ns after an edge). +; - +; THE NEGATIVE CONTROLS (FR-004), each a test that passes by seeing the failure, and ONE THAT DOES NOT: the limit of the check. The pull-up of x4 (its two PMOS) dead: Q's 1 level at 0 V, 1.5 V away; the NMOS table +; dead: Q's 0 level at 1.5 V, and Q's 1 level 1.4 mV off its rail; the pull-up of x4 at a HUNDREDTH of its width (`dq_scale`, 435412 words off the log table): Q's 1 level 186.4 uV, 171.8 uV from ngspice's, 17 times +; the bar: refused. THE LIMIT: the pull-up at a TENTH of its width (217706 words, log2 10 octaves): Q's 1 level 18.656 uV, 10.1 times ours and 4.07 uV and 0.50 uV from ngspice's capture 1 and set, so all +; eight levels are inside the bar (the worst gap 4.07 uV): the check ACCEPTS the cell with a pull-up ten times too weak, and refuses the cell as the tables give it. The width control the issue asks for (a tenth +; of the width refused) is not met on these tables, and the reason is the one above: the level that ngspice reads at Q's 1 includes a load current worth 13 to 16 uV, and a pull-up ten times weaker lands on it by +; accident. So the bar of 10 uV is NOT loosened to make the row a PASS, and no PASS is claimed: a PASS here would be as likely a wrong cell as a right one. +; - +; WHAT THE RESULT SAYS. On the tt 27 C PSP103 log tables, with the gate current absent, the dff's Q and nQ sit within 2 uV of a rail in every stored state, inside the report's 50 mV by 27000 times, and six of +; the eight are within 1.33 uV of ngspice's own run of the same cell. WHAT IT DOES NOT SAY. That two of the eight are within the bar (they are 12.7 and 16.3 uV under it); that the gate current is the cause; the +; levels of net1 to net5 (no ngspice number); another corner, temperature or load on Q or nQ; the dff with its clock high, or a level in a transition; speed (the clock-to-Q is the first stage of the sequencer in +; accept_sar.t27). The model is a TABLE MODEL of PSP103 without the gate current, not PSP103. ASCII only, English only. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module AnalogSarDffQ; + +use analog::linear::{fabs64}; +use analog::mos_table::{VDD, lg_fine, dpos, fmax64, LG_R_OFF, LG_LEN}; +use analog::mos_psp103::{UPV, IUNIT_A, dc_out}; +use analog::mos_psp103_data::{TT_LG_N, TT_LG_P}; +use analog::mos_psp103_gates::{GT_DV, GT_B_LEVEL_V, gt_nor, gt_tg_ron, gt_tg_off}; + +pub const KIND : str = "analog-cell-check"; +pub const ID : str = "analog/sar_dff_q"; +pub const REFERENCE : str = "IHP-Open-PDK 21133f21 sg13_lv_nmos/pmos (PSP103) through ngspice-47; deck specs/analog/decks/accept_dff.cir"; + +/// The four stored states the deck measures, in the order of its prints: after a reset (Reset high then low, D 1.5 V), after a capture of 1 (D = 1.5 V sampled, D then 0), after a capture of 0 +/// (D = 0 sampled) and after a set (Set high then low, D 0). The clock is low and Reset and Set are low in all four. +pub const DQ_RESET : u32 = 0; +pub const DQ_CAP1 : u32 = 1; +pub const DQ_CAP0 : u32 = 2; +pub const DQ_SET : u32 = 3; + +; --- ngspice-47 + PSP103 mos_tt 27 C, specs/analog/decks/accept_dff.cir (6 digits as printed, none changed) -------------------------------- +/// Q's distance from its rail in V in the four states (for a 1, 1.5 V minus Q; for a 0, Q itself): reset, capture 1, capture 0, set. +pub const DQ_NG_Q : [4]f64 = [1.39007e-06, 1.45814e-05, 1.39011e-06, 1.81517e-05]; +/// nQ's, in the same order. +pub const DQ_NG_NQ : [4]f64 = [7.2068e-07, 6.35462e-07, 7.20689e-07, 6.37802e-07]; + +; --- the stored states ----------------------------------------------------------------------------------------------- +/// A stored 1 in state s: after a capture of 1 and after a set. +pub fn dq_one(s: u32) -> bool { + return s == DQ_CAP1 or s == DQ_SET; +} + +/// D is high (1.5 V) when the state is read: only in the reset state (D falls at 40 ns and the other three are read after it). +pub fn dq_d_high(s: u32) -> bool { + return s == DQ_RESET; +} + +/// The rail of a node: VDD for a 1, 0 V for a 0, in words of 2^-24 V. +pub fn dq_rail(high: bool) -> u32 { + if (high) { return VDD; } + return 0; +} + +/// A node's distance from the rail it should sit on, in V: for a 1 the supply minus the node, for a 0 the node (the digits fall on the deviation and not on the 1.5 V). +pub fn dq_dev(w: u32, high: bool) -> f64 { + if (high) { return ((VDD as f64) - (w as f64)) / UPV; } + return (w as f64) / UPV; +} + +/// A node moved dw words away from its rail (a 1 down, a 0 up), at most to the other rail. +pub fn dq_away(w: u32, high: bool, dw: u32) -> u32 { + if (high) { return dpos(w as u64, dw as u64) as u32; } + if (w + dw > VDD) { return VDD; } + return w + dw; +} + +/// A voltage in words from a current and a resistance, rounded; a dead device (no current, an infinite or undefined resistance) is the supply. +pub fn dq_words(i_a: f64, r_ohm: f64) -> u32 { + const w : f64 = i_a * r_ohm * UPV + 0.5; + if (w < (VDD as f64)) { return w as u32; } + return VDD; +} + +/// The output resistance of a nor whose output sits on a rail through its on devices, at a small signal (10 mV): a 1 through the two PMOS in series (both inputs low), a 0 through one NMOS. +pub fn dq_rout(tn: []const u32, tp: []const u32, one: bool) -> f64 { + const dv : f64 = (GT_DV as f64) / UPV; + if (one) { return 2.0 * dv / ((lg_fine(tp, VDD, GT_DV) as f64) * IUNIT_A); } + return dv / ((lg_fine(tn, VDD, GT_DV) as f64) * IUNIT_A); +} + +/// The table t with every current divided by 2^(cut / 65536) (the log words lowered by cut, floored at 0 = 1 IUNIT; the tag and the rv words unchanged): a device of that fraction of the +/// width, in the weak and the strong region alike. out has the length of a log table, 4851 words. cut = 217706 is a tenth (log2 10 = 3.32193 octaves of 65536 words), 435412 a hundredth. +pub fn dq_scale(t: []const u32, out: []u32, cut: u32) { + out[0] = t[0]; + var i : usize = 1; + while (i < LG_R_OFF) { + out[i] = dpos((t[i]) as u64, (cut as u64)) as u32; + i = i + 1; + } + while (i < LG_LEN) { + out[i] = t[i]; + i = i + 1; + } +} + +; --- the solve --------------------------------------------------------------------------------------------------------- +/// The nodes of the dff in a stored state, in words of 2^-24 V. +pub struct DqNodes { net1: u32, net2: u32, net3: u32, net4: u32, net5: u32, q: u32, nq: u32 } + +/// The nodes of the stored 1 (one) or the stored 0, with D at the rail d_high, the clock low and Reset and Set low, by `iters` sweeps from the ideal rails. tn and tp are the tables of every device; +/// tp4 is the PMOS table of x4's pull-up alone (the same as tp for the cell, another for a control). +pub fn dq_solve_k(tn: []const u32, tp: []const u32, tp4: []const u32, one: bool, d_high: bool, iters: u32) -> DqNodes { + // the rails: with the clock low x1 and x7 are on (D reaches net1, net4 reaches net3) and x3 and x9 are off; Reset and Set are low, so net2 = nor(0, D) is high when D is low, net5 = nor(net2, 0) is + // its complement, net4 = nor(Q, 0) and net3 are the complement of Q. x9 has net5 on one side and net1 = D on the other: the same rail in every state, so it leaks nothing + const hi2 : bool = !d_high; + const hi3 : bool = !one; + var n1 : u32 = dq_rail(d_high); + var n2 : u32 = dq_rail(hi2); + var n3 : u32 = dq_rail(hi3); + var n4 : u32 = dq_rail(hi3); + var n5 : u32 = dq_rail(d_high); + var q : u32 = dq_rail(one); + var nq : u32 = dq_rail(hi3); + // the off x3 has net2 on one side and net3 on the other: on opposite rails (D against the stored bit) it leaks gt_tg_off, 1.5 V across it, and the current flows through x2's on devices on net2's side + // and through x7 (on) and x5's on devices on net3's side, which moves both nodes away from their rails + var i_leak : f64 = 0.0; + if (one != d_high) { i_leak = gt_tg_off(tn, tp); } + var v7 : u32 = 0; + if (hi3) { v7 = VDD - GT_DV; } + const dw2 : u32 = dq_words(i_leak, dq_rout(tn, tp, hi2)); + const dw4 : u32 = dq_words(i_leak, dq_rout(tn, tp, hi3)); + const dw7 : u32 = dq_words(i_leak, gt_tg_ron(tn, tp, v7, true)); + var it : u32 = 0; + while (it < iters) { + n2 = dq_away(gt_nor(tn, tp, 0, n1), hi2, dw2); + n5 = gt_nor(tn, tp, n2, 0); + q = gt_nor(tn, tp4, 0, n3); + nq = dc_out(tn, tp, q); + n4 = dq_away(gt_nor(tn, tp, q, 0), hi3, dw4); + n3 = dq_away(n4, hi3, dw7); + it = it + 1; + } + return DqNodes { net1: n1, net2: n2, net3: n3, net4: n4, net5: n5, q: q, nq: nq }; +} + +/// The solve with the three sweeps it needs: a node moves only by the words of a rail-to-rail gate level or of a leakage times a resistance, so the sweep reaches its fixed point at once and a test +/// pins that more sweeps change nothing. +pub fn dq_solve(tn: []const u32, tp: []const u32, tp4: []const u32, one: bool, d_high: bool) -> DqNodes { + return dq_solve_k(tn, tp, tp4, one, d_high, 3); +} + +; --- the comparison ---------------------------------------------------------------------------------------------------- +/// Our Q and nQ in a state, as the distance from their rails in V, and the signed gap to ngspice's (ours minus its). +pub struct DqPair { q_v: f64, nq_v: f64, gq_v: f64, gnq_v: f64 } + +pub fn dq_pair(tn: []const u32, tp: []const u32, tp4: []const u32, s: u32) -> DqPair { + const one : bool = dq_one(s); + const d : DqNodes = dq_solve(tn, tp, tp4, one, dq_d_high(s)); + const q : f64 = dq_dev(d.q, one); + const nq : f64 = dq_dev(d.nq, !one); + return DqPair { q_v: q, nq_v: nq, gq_v: q - DQ_NG_Q[s as usize], gnq_v: nq - DQ_NG_NQ[s as usize] }; +} + +/// The eight levels: worst_v, the largest distance of any of our eight from its rail (the report's check, 50 mV), gap_v, the largest distance of any of ours from ngspice's (the cross-check, 10 uV), +/// and n_in, how many of the eight are within that bar of ngspice's. +pub struct DqLv { worst_v: f64, gap_v: f64, n_in: u32 } + +pub fn dq_levels(tn: []const u32, tp: []const u32, tp4: []const u32) -> DqLv { + var worst : f64 = 0.0; + var gap : f64 = 0.0; + var n_in : u32 = 0; + var s : u32 = 0; + while (s < 4) { + const p : DqPair = dq_pair(tn, tp, tp4, s); + worst = fmax64(worst, fmax64(p.q_v, p.nq_v)); + gap = fmax64(gap, fmax64(fabs64(p.gq_v), fabs64(p.gnq_v))); + if (fabs64(p.gq_v) <= GT_B_LEVEL_V) { n_in = n_in + 1; } + if (fabs64(p.gnq_v) <= GT_B_LEVEL_V) { n_in = n_in + 1; } + s = s + 1; + } + return DqLv { worst_v: worst, gap_v: gap, n_in: n_in }; +} + +; --- the tests ---------------------------------------------------------------------------------------------------- +; Variables, not constants, carry the data of the asserts: an assert on a constant is folded by the compiler and counts as no assert at all. + +test the_dff_nodes_sit_microvolts_from_their_rails_in_the_three_stored_circuits_and_a_first_and_a_sixth_sweep_change_no_word { + // the stored 0 after a reset (D 1.5 V), the distances from the rail in words (59.6 nV): net1 is D, exactly; net2 7 (the gate level, 1 word, and 6 words = 0.36 uV: the leakage of the off x3, 291 pA, through x2's + // NMOS, 1301 ohm); net3 57 and net4 48 (the nor 00 level 31 words, plus 17 words = 1.04 uV through x5's two PMOS in series, 3583 ohm, and 9 more through x7, 1791 ohm); net5 31 (the nor 00 level); Q 1; nQ 5 + const r : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, false, true); + assert_eq(r.net1, VDD); + assert_eq(r.net2, 7); + assert_eq(VDD - r.net3, 57); + assert_eq(VDD - r.net4, 48); + assert_eq(VDD - r.net5, 31); + assert_eq(r.q, 1); + assert_eq(VDD - r.nq, 5); + // the stored 0 after a capture of 0 (D 0 V): net2 and x3's other side are on the same rail, no leakage, so every node is its gate's level: net2, net3 and net4 31 words, net5 and Q 1 word, nQ 5 + const c0 : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, false, false); + assert_eq(c0.net1, 0); + assert_eq(VDD - c0.net2, 31); + assert_eq(VDD - c0.net3, 31); + assert_eq(VDD - c0.net4, 31); + assert_eq(c0.net5, 1); + assert_eq(c0.q, 1); + assert_eq(VDD - c0.nq, 5); + // the stored 1 (D 0 V): net2 48 (31 words, and 17 words under it through x2's two PMOS in series), net3 13 and net4 7 (1 word, and 6 words = 0.36 uV through x5's NMOS and 6 more through x7's NMOS), net5 1, + // Q 31 (the nor 00 level), nQ 2 + const o : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, true, false); + assert_eq(o.net1, 0); + assert_eq(VDD - o.net2, 48); + assert_eq(o.net3, 13); + assert_eq(o.net4, 7); + assert_eq(o.net5, 1); + assert_eq(VDD - o.q, 31); + assert_eq(o.nq, 2); + // the sweep is at its fixed point: one and six sweeps give the same seven words, in the two circuits that carry the leakage of x3 + const r6 : DqNodes = dq_solve_k(&TT_LG_N, &TT_LG_P, &TT_LG_P, false, true, 6); + const o6 : DqNodes = dq_solve_k(&TT_LG_N, &TT_LG_P, &TT_LG_P, true, false, 6); + const r1 : DqNodes = dq_solve_k(&TT_LG_N, &TT_LG_P, &TT_LG_P, false, true, 1); + const o1 : DqNodes = dq_solve_k(&TT_LG_N, &TT_LG_P, &TT_LG_P, true, false, 1); + assert_eq(r6.net2, r.net2); + assert_eq(r6.net3, r.net3); + assert_eq(r6.net4, r.net4); + assert_eq(r6.net5, r.net5); + assert_eq(r6.q, r.q); + assert_eq(r6.nq, r.nq); + assert_eq(o6.net2, o.net2); + assert_eq(o6.net3, o.net3); + assert_eq(o6.net4, o.net4); + assert_eq(o6.net5, o.net5); + assert_eq(o6.q, o.q); + assert_eq(o6.nq, o.nq); + assert_eq(r1.net3, r.net3); + assert_eq(r1.q, r.q); + assert_eq(o1.net3, o.net3); + assert_eq(o1.net4, o.net4); + assert_eq(o1.q, o.q); + assert_eq(o1.nq, o.nq); +} + +test the_three_circuits_have_one_answer_each_and_the_off_x9_carries_nothing_and_x4s_input_net3_is_off_its_rail_and_nothing_moves { + // reset and capture 0 are one stored 0 (they differ by D, which reaches net1, net2 and net5 and not Q or nQ): the same Q and nQ words; capture 1 and set are one circuit: the same seven words + const s0 : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, dq_one(DQ_RESET), dq_d_high(DQ_RESET)); + const s2 : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, dq_one(DQ_CAP0), dq_d_high(DQ_CAP0)); + const s1 : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, dq_one(DQ_CAP1), dq_d_high(DQ_CAP1)); + const s3 : DqNodes = dq_solve(&TT_LG_N, &TT_LG_P, &TT_LG_P, dq_one(DQ_SET), dq_d_high(DQ_SET)); + assert_eq(s0.q, s2.q); + assert_eq(s0.nq, s2.nq); + assert_eq(s1.q, s3.q); + assert_eq(s1.nq, s3.nq); + assert_eq(s1.net1, s3.net1); + assert_eq(s1.net2, s3.net2); + assert_eq(s1.net3, s3.net3); + assert_eq(s1.net4, s3.net4); + assert_eq(s1.net5, s3.net5); + assert(dq_one(DQ_CAP1)); + assert(dq_one(DQ_SET)); + assert(!dq_one(DQ_RESET)); + assert(!dq_one(DQ_CAP0)); + assert(dq_d_high(DQ_RESET)); + assert(!dq_d_high(DQ_CAP1)); + // x9 off: net5 and net1 sit on the same rail in every state, 31 words apart at the most (the reset: net5 is 31 words under the supply, net1 is the supply), and at that voltage the off tgate leaks less + // than half an IUNIT (1.164 pA): no current to a word. x1 on: net1 is D, exactly + var dv0 : u32 = VDD - s0.net5; + var dv1 : u32 = s1.net5; + assert_eq(dv0, 31); + assert_eq(dv1, 1); + assert_eq(lg_fine(&TT_LG_N, 0, dv0) + lg_fine(&TT_LG_P, 0, dv0), 0); + assert_eq(lg_fine(&TT_LG_N, 0, dv1) + lg_fine(&TT_LG_P, 0, dv1), 0); + assert_eq(s0.net1, VDD); + assert_eq(s1.net1, 0); + assert_eq(s2.net1, 0); + // x4's input net3 sits 57 words (3.4 uV) off its rail in the stored 0 after a reset and 13 words off it in the stored 1, and nothing moves: Q is the gate level with the input exactly on the rail, in both + // circuits, and nQ is the inverter on Q + var lo : u32 = 0; + var hi : u32 = VDD; + assert_eq(s0.q, gt_nor(&TT_LG_N, &TT_LG_P, lo, hi)); + assert_eq(s1.q, gt_nor(&TT_LG_N, &TT_LG_P, lo, lo)); + assert_eq(s0.nq, dc_out(&TT_LG_N, &TT_LG_P, s0.q)); + assert_eq(s1.nq, dc_out(&TT_LG_N, &TT_LG_P, s1.q)); + // the order of x4's two inputs is not resolved by the repository and does not matter: nor(0, 1) and nor(1, 0) are the same word (1 word over ground), the inputs at the rails + assert_eq(gt_nor(&TT_LG_N, &TT_LG_P, lo, hi), gt_nor(&TT_LG_N, &TT_LG_P, hi, lo)); + assert_eq(gt_nor(&TT_LG_N, &TT_LG_P, lo, hi), 1); +} + +test the_eight_levels_against_ngspice_six_are_within_1_4_uV_and_q_at_1_after_a_capture_of_1_and_after_a_set_is_12_7_and_16_3_uV_under { + // reset: Q 0.0596 uV against 1.3901, nQ (a 1) 0.2980 uV against 0.7207 + const p0 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_RESET); + assert(p0.q_v > 0.0000000596); + assert(p0.q_v < 0.0000000597); + assert(p0.nq_v > 0.000000298); + assert(p0.nq_v < 0.0000002981); + assert(p0.gq_v > 0.0 - 0.0000013305); + assert(p0.gq_v < 0.0 - 0.0000013304); + assert(p0.gnq_v > 0.0 - 0.00000042266); + assert(p0.gnq_v < 0.0 - 0.00000042265); + // capture 1: Q (a 1) 1.8477 uV against 14.5814, nQ (a 0) 0.1192 uV against 0.6355 + const p1 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_CAP1); + assert(p1.q_v > 0.0000018477); + assert(p1.q_v < 0.0000018478); + assert(p1.nq_v > 0.00000011920); + assert(p1.nq_v < 0.00000011921); + assert(p1.gq_v > 0.0 - 0.0000127337); + assert(p1.gq_v < 0.0 - 0.0000127336); + assert(p1.gnq_v > 0.0 - 0.00000051626); + assert(p1.gnq_v < 0.0 - 0.00000051625); + // capture 0: Q 0.0596 uV against 1.3901, nQ 0.2980 uV against 0.7207 + const p2 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_CAP0); + assert(p2.q_v > 0.0000000596); + assert(p2.q_v < 0.0000000597); + assert(p2.nq_v > 0.000000298); + assert(p2.nq_v < 0.0000002981); + assert(p2.gq_v > 0.0 - 0.0000013306); + assert(p2.gq_v < 0.0 - 0.0000013305); + assert(p2.gnq_v > 0.0 - 0.00000042267); + assert(p2.gnq_v < 0.0 - 0.00000042266); + // set: Q 1.8477 uV against 18.1517, nQ 0.1192 uV against 0.6378 + const p3 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_SET); + assert(p3.q_v > 0.0000018477); + assert(p3.q_v < 0.0000018478); + assert(p3.nq_v > 0.00000011920); + assert(p3.nq_v < 0.00000011921); + assert(p3.gq_v > 0.0 - 0.0000163040); + assert(p3.gq_v < 0.0 - 0.0000163039); + assert(p3.gnq_v > 0.0 - 0.00000051860); + assert(p3.gnq_v < 0.0 - 0.00000051859); + // the six inside the bar are inside it by 7 times or more (the largest gap of the six, 1.3306 uV); the two Q levels at 1 are 1.27 and 1.63 times the bar outside it + assert(fabs64(p0.gq_v) * 7.0 < GT_B_LEVEL_V); + assert(fabs64(p2.gq_v) * 7.0 < GT_B_LEVEL_V); + assert(fabs64(p0.gnq_v) * 20.0 < GT_B_LEVEL_V); + assert(fabs64(p1.gnq_v) * 19.0 < GT_B_LEVEL_V); + assert(fabs64(p2.gnq_v) * 20.0 < GT_B_LEVEL_V); + assert(fabs64(p3.gnq_v) * 19.0 < GT_B_LEVEL_V); + assert(fabs64(p1.gq_v) > 1.27 * GT_B_LEVEL_V); + assert(fabs64(p1.gq_v) < 1.28 * GT_B_LEVEL_V); + assert(fabs64(p3.gq_v) > 1.63 * GT_B_LEVEL_V); + assert(fabs64(p3.gq_v) < 1.64 * GT_B_LEVEL_V); + // the cross-check: the worst distance from a rail of ours is 1.8477 uV, the largest gap 16.304 uV, six of the eight inside the bar + const lv : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &TT_LG_P); + assert(lv.worst_v > 0.0000018477); + assert(lv.worst_v < 0.0000018478); + assert(lv.gap_v > 0.0000163039); + assert(lv.gap_v < 0.0000163040); + assert_eq(lv.n_in, 6); + assert(lv.gap_v > GT_B_LEVEL_V); + // ngspice's own worst level is 18.1517 uV: the report's 50 mV is 2750 times above it and 27000 times above ours + var ng_worst : f64 = 0.0; + var k : u32 = 0; + while (k < 4) { + ng_worst = fmax64(ng_worst, fmax64(DQ_NG_Q[k as usize], DQ_NG_NQ[k as usize])); + k = k + 1; + } + assert(ng_worst > 0.00001815); + assert(ng_worst < 0.00001816); + var limit : f64 = 0.05; + assert(ng_worst * 2700.0 < limit); + assert(lv.worst_v * 27000.0 < limit); +} + +test every_one_of_the_eight_gaps_is_negative_the_sign_of_a_dc_current_the_tables_do_not_carry_and_the_current_it_would_take_is_a_few_nA_at_q_when_it_is_a_1 { + var k : u32 = 0; + var neg : u32 = 0; + while (k < 4) { + const p : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, k); + if (p.gq_v < 0.0) { neg = neg + 1; } + if (p.gnq_v < 0.0) { neg = neg + 1; } + k = k + 1; + } + assert_eq(neg, 8); + // the table's own resistance at the node: two PMOS in series at a 1, one NMOS at a 0 (10 mV across, full gate drive) + const rp : f64 = dq_rout(&TT_LG_N, &TT_LG_P, true); + const rn : f64 = dq_rout(&TT_LG_N, &TT_LG_P, false); + assert(rp > 3582.5); + assert(rp < 3582.6); + assert(rn > 1301.4); + assert(rn < 1301.5); + // the current that would close the gap: gap over resistance, 3.554 nA (capture 1) and 4.551 nA (set) at Q's 1, 1.022 nA at Q's 0 (reset and capture 0), 0.236 nA at nQ's 1 (one PMOS: half of rp) + // and 0.397 and 0.399 nA at nQ's 0 + const p0 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_RESET); + const p1 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_CAP1); + const p3 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_SET); + const i_c1 : f64 = (0.0 - p1.gq_v) / rp; + const i_set : f64 = (0.0 - p3.gq_v) / rp; + const i_q0 : f64 = (0.0 - p0.gq_v) / rn; + const i_nq1 : f64 = (0.0 - p0.gnq_v) / (rp / 2.0); + const i_nq0 : f64 = (0.0 - p1.gnq_v) / rn; + assert(i_c1 > 0.000000003554); + assert(i_c1 < 0.000000003555); + assert(i_set > 0.000000004550); + assert(i_set < 0.000000004552); + assert(i_q0 > 0.000000001022); + assert(i_q0 < 0.000000001023); + assert(i_nq1 > 0.000000000235); + assert(i_nq1 < 0.000000000237); + assert(i_nq0 > 0.000000000396); + assert(i_nq0 < 0.000000000398); + // the leakage of the off x1 that IS in the tables, for scale: 291.04 pA, 12 to 16 times smaller than what Q's 1 level would need + const leak : f64 = gt_tg_off(&TT_LG_N, &TT_LG_P); + assert(leak > 0.000000000291); + assert(leak < 0.0000000002911); + assert(i_c1 > 12.0 * leak); + assert(i_set > 15.0 * leak); +} + +test ngspices_own_two_reads_of_one_stored_state_differ_by_3_57_uV_and_the_two_reads_of_the_other_agree_to_a_hundredth_of_a_nanovolt { + // capture 1 and set are the same circuit (D 0 V, Reset and Set low, the clock low): Q 14.5814 and 18.1517 uV, 3.5703 uV apart; nQ 0.635462 and 0.637802 uV, 2.34 nV apart + var i_set : usize = DQ_SET as usize; + var i_c1 : usize = DQ_CAP1 as usize; + var i_c0 : usize = DQ_CAP0 as usize; + var i_rs : usize = DQ_RESET as usize; + const dq : f64 = DQ_NG_Q[i_set] - DQ_NG_Q[i_c1]; + const dn : f64 = DQ_NG_NQ[i_set] - DQ_NG_NQ[i_c1]; + assert(dq > 0.0000035702); + assert(dq < 0.0000035704); + assert(dn > 0.0000000023); + assert(dn < 0.0000000024); + // reset and capture 0 are the other circuit: Q 1.39007 and 1.39011 uV, 0.04 nV apart; nQ 0.72068 and 0.720689 uV, 9 pV apart + const rq : f64 = DQ_NG_Q[i_c0] - DQ_NG_Q[i_rs]; + const rn : f64 = DQ_NG_NQ[i_c0] - DQ_NG_NQ[i_rs]; + assert(rq > 0.00000000003); + assert(rq < 0.00000000005); + assert(rn > 0.000000000008); + assert(rn < 0.00000000001); + // a DC solve has one answer for the pair, so no model can be at the same time within 0 of both reads; the bar (10 uV) is 2.8 times the spread + assert(GT_B_LEVEL_V > 2.8 * dq); + assert(GT_B_LEVEL_V < 2.9 * dq); +} + +test a_dead_pull_up_or_a_dead_nmos_or_a_pull_up_at_a_hundredth_of_the_width_is_refused_and_a_pull_up_at_a_tenth_of_the_width_is_not_that_is_the_limit_of_the_level_check { + var dead : [4851]u32 = [0] ** 4851; + var tenth : [4851]u32 = [0] ** 4851; + var hundredth : [4851]u32 = [0] ** 4851; + dq_scale(&TT_LG_P, &tenth, 217706); + dq_scale(&TT_LG_P, &hundredth, 435412); + // the scaled tables are a tenth and a hundredth of the current at full drive (258951843 IUNIT at 1.5 V, 1.5 V), to a part in a million, with the tag and the rv words untouched + const full : u64 = lg_fine(&TT_LG_P, VDD, VDD); + const t10 : u64 = lg_fine(&tenth, VDD, VDD); + const t100 : u64 = lg_fine(&hundredth, VDD, VDD); + assert(full > 258951000); + assert(full < 258952000); + assert(t10 * 10 > full - 1000); + assert(t10 * 10 < full + 1000); + assert(t100 * 100 > full - 10000); + assert(t100 * 100 < full + 10000); + assert_eq(tenth[0], TT_LG_P[0]); + assert_eq(tenth[4800], TT_LG_P[4800]); + // NEGATIVE CONTROL, the pull-up of x4 dead (a zero table in its slot): Q at a 1 never rises, it is 1.5 V from its rail, and so is nQ; the gap to ngspice's is 1.5 V, 150000 times the bar + const d4 : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &dead); + assert(d4.worst_v > 1.4999); + assert(d4.gap_v > 1.4999); + assert(d4.gap_v > 100000.0 * GT_B_LEVEL_V); + assert_eq(d4.n_in, 4); + // NEGATIVE CONTROL, no NMOS conducts: Q at a 0 stays at 1.5 V (the pull-up wins), Q at a 1 is 1.4 mV off its rail, nQ at a 0 stays at the supply (1.5 V off) + const dn : DqLv = dq_levels(&dead, &TT_LG_P, &TT_LG_P); + assert(dn.worst_v > 1.4999); + assert(dn.gap_v > 1.4999); + assert_eq(dn.n_in, 2); + // NEGATIVE CONTROL, the pull-up of x4 at a HUNDREDTH of its width: Q at a 1 is 186.38 uV from its rail, 171.8 and 168.2 uV from ngspice's two reads, 17 times the bar: refused + const h : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &hundredth); + const h1 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &hundredth, DQ_CAP1); + assert(h1.q_v > 0.000186383); + assert(h1.q_v < 0.000186384); + assert(h.gap_v > 0.0001718); + assert(h.gap_v < 0.0001719); + assert(h.gap_v > 17.0 * GT_B_LEVEL_V); + assert_eq(h.n_in, 6); + // THE LIMIT OF THE CHECK: the pull-up at a TENTH of its width. Q at a 1 is 18.656 uV, 10.097 times ours (the model sees the width: Q at a 1 is the NMOS leakage over the pull-up's resistance), and it + // is +4.07 uV from ngspice's capture 1 and +0.50 uV from its set: all eight levels are inside the 10 uV bar, the worst gap 4.07 uV. The check refuses the cell as the tables give it (gap 16.30 uV, + // above) and ACCEPTS the one with a pull-up ten times too weak + const t : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &tenth); + const t1 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &tenth, DQ_CAP1); + const t3 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &tenth, DQ_SET); + const b1 : DqPair = dq_pair(&TT_LG_N, &TT_LG_P, &TT_LG_P, DQ_CAP1); + assert(t1.q_v > 0.000018656); + assert(t1.q_v < 0.000018657); + assert(t1.q_v > 10.09 * b1.q_v); + assert(t1.q_v < 10.10 * b1.q_v); + assert(t1.gq_v > 0.0000040748); + assert(t1.gq_v < 0.0000040749); + assert(t3.gq_v > 0.00000050455); + assert(t3.gq_v < 0.00000050456); + assert(t.gap_v > 0.0000040748); + assert(t.gap_v < 0.0000040749); + assert(t.gap_v < GT_B_LEVEL_V); + assert_eq(t.n_in, 8); + const base : DqLv = dq_levels(&TT_LG_N, &TT_LG_P, &TT_LG_P); + assert(base.gap_v > GT_B_LEVEL_V); + assert(base.gap_v > t.gap_v); +}