- Python 60.3%
- DM 36.7%
- Java 3%
Reads R_PD = J_N/J_P from pygmid lookup at VGS=VDD, builds the tables first if missing (runs pygmid.sweep on mypdk.cfg), then sets Wp = R_PD*Wn with Wn fixed at the 120 nm DRC floor for balanced rise/fall. Assisted-by: pi:glm-5.2 |
||
|---|---|---|
| cadence/Project_1 | ||
| .envrc.default | ||
| .gitignore | ||
| AGENTS.md | ||
| balance.py | ||
| full_adder.scs | ||
| monte_carlo.csv | ||
| propagation_delay.csv | ||
| README.md | ||
| report.md | ||
| size_adder.py | ||
Full Adder — Manual Spectre Testing Plan
Verification of the 28T mirror adder (full_adder.scs) sized by size_adder.py.
The logical-effort model assumes a step input and ignores slew, body effect in
stacks, and Miller coupling -- a transient simulation is required to confirm
operation under a 1 GHz square wave. This is a hand-driven plan (you build the
testbench).
Spec (the actual pass criterion)
The adder is driven by a 1 GHz square wave (1 ns period, 100 ps rise/fall, 0 <-> 1.1 V) whose output is consumed by a following stage. Pass = every output reaches a valid logic level (next stage's VIH/VIL) before the next clock edge, i.e. within one period less setup/margin. The budget is therefore period-based, ~850 ps, NOT a 250 ps propagation-delay limit:
T_budget = T_clock - T_setup_next - T_skew - margin
~= 1000 ps - ~50 ps - ~0 - ~100 ps ~= 850 ps
Measurements that matter, in order of strictness:
- 50% crossing (tpLH/tpHL): must be well under 850 ps; under ~700 ps is comfortable. This is the conventional propagation-delay number.
- VIH/VIL crossing (e.g. ~0.7-0.8*VDD ~= 770-880 mV): the actual validity instant for the next stage; must be under 850 ps.
- Full swing to 1.1 V: settling time, informational; under 850 ps is best but not strictly required (the next stage samples at VIH, not VDD).
A 50% crossing at 337 ps and full swing at 500 ps (measured on the direct sum path) passes -- there is ~500 ps of margin.
Sizing to use
From size_adder.py at VGS = VDD = 1.1 V, floored to the foundry DRC
minimum W_MIN = 120 nm, balanced Wp/Wn = R_PD = 1.80:
Wn = 120 nm Wp = 215 nm (base unit widths, X = Y = 1)
Set these on the subckt call (they override the stale parameters line in
full_adder.scs, which still reads Wn=90n Wp=141n from the gm/ID=10 run):
parameters Wn=120n Wp=215n
Xfa A B C_I C_O S vdd gnd full_adder Wn=120n Wp=215n
All device widths follow from the literal schematic multipliers (2x, 3x, 4x, 6x)
of Wn/Wp. L = 45 nm throughout. All PMOS bulks to vdd, all NMOS bulks to
gnd (the committed netlist is correct; a testbench that ties PMOS bulk to gnd
forward-biases the source-body diode and strands the output below VDD).
Testbench setup (build by hand)
- Models.
includethe same PDK model file used for the pygmid sweep (thench/pchBSIM4 models). Settnom = 27. - Supply.
vddnet = 1.1 V via an idealvdc.gnd= 0. - Inputs A, B, C_I. Three
vpulse/vpwlsources, 0 <-> 1.1 V, with 100 ps rise and fall times (the spec). Drive them directly — no input buffer, so the 100 ps ramp lands on the adder inputs unmodified. - Loads.
C_LOAD = 10 fFfromC_Oto gnd and fromSto gnd (both outputs, per spec). - Analysis.
tran tran step=1p stop=5n icnode=all(or whatever your kit needs for clean 50% crossings). Optionallynoiseoff,reltoltight (1e-5) so delay measurements are stable. - Measurements.
cross/delaystatements for 50% points:tpLH(C_O),tpHL(C_O)vs the toggling input that causes the transitiontpLH(S),tpHL(S)likewise- measure from the toggling input's 50% to the output's 50%.
Worst-case vectors (the paths the model flagged)
Pick the input edge that exercises each critical path. Two paths matter:
| Path | Step-model delay | Exercised by |
|---|---|---|
| A -> Cout = carry_gate + carry_inv | 192.6 ps | A (or B) rising with Cout rising |
| A -> S (via X2) = carry + sum + final_inv | 199.6 ps (worst) | A/B/Cin edge that propagates through X2 then the sum 3-stack |
Vectors to run (start each from a settled DC state, ~1 ns settle between edges):
- Cout rising (A->Cout): set
C_I=0, B=0, pulseA0->1.1->0.Cout = A(since B=Ci=0) -> measurestpLH(C_O)andtpHL(C_O). - Cout via carry chain:
A=1, B=0, pulseC_I0->1.1.Cout = Ci, exercises the M6/M7 stack ->tpLH(C_O). - Sum via X2 (worst, 3-stack + carry):
A=1, B=1, pulseC_I0->1.1.Coutrises (1) andSumfalls through the sum PDN 3-stack driven by the carry-ctrl ->tpHL(S). This is the predicted worst path. - Sum direct (bypass X2):
C_I=0, pulseA(and/orB) to toggle the majority term ->tpHL(S)/tpLH(S).
Run each for both rising and falling input edges (rise and fall differ because PMOS and NMOS stacks are not identical even when balanced).
Configurations to sweep
Three points, in order. Only the two output inverters change (everything else stays at the base widths):
| Config | Sum final inv (MSI) | Carry inv (M11/12) | Why |
|---|---|---|---|
| C1 base | 1x Wp / 1x Wn | 1x Wp / 1x Wn | First to try. Direct-sum-path measurement (337 ps 50%) already passes the period budget; if the worst path (via X2) also passes, ship this. |
| C2 light buffer | 4x Wp / 4x Wn | 1x Wp / 1x Wn | If C1 misses on a path: drops the Sum output inverter below the ramp floor so it stops being RC-limited. Targeted at Sum. |
| C3 buffer both | 4x Wp / 4x Wn | 2x Wp / 2x Wn | If Cout is the bottleneck (not Sum): mildly relieves the carry inverter without loading X2 much. |
Implement by overriding the inverter multipliers on the instance, or by adding
X/Y parameters to the subckt. Do not use the script's reported optimum
(X=12, Y=6 etc.) -- that is a step-model artifact (monotonic sweep, no minimum
in range); with finite slew, widening past X~4 buys nothing because the gate
becomes slope-limited.
Predicted vs measured (the comparison table to fill)
Step-model delays are from size_adder.py (step input, no buffer, no slew).
The sloped prediction is rough (0.4 * t_in per stage, propagated through the
input double buffer). Expect Spectre to be ~50-150 ps above the sloped
prediction due to 3-stack body effect (VSB>0 on upper devices), Miller, and
partial gate drive during the ramp -- effects LE abstracts away. The pass
column is against the 850 ps period budget, not 250 ps.
| Path | Step model (ps) | Sloped+buf (ps) | Measured 50% (ps) | Measured full-swing (ps) | Pass (<850 ps) |
|---|---|---|---|---|---|
| A->Cout | 77.5 | ~200-280 | |||
| A->S via X2 (worst) | 84.5 | ~250-350 | |||
| A->S direct | 78.1 | ~250-350 | 337 | ~500 | PASS (margin ~500 ps) |
The direct-path measurement (337 ps / 500 ps, vector A=1.1 B=C=0) already passes comfortably. The binding unknown is the via-X2 path (vector A=B=1, pulse Ci).
Decision criteria (against the 850 ps period budget)
- C1 passes on all paths (every 50% crossing < ~700 ps, full swing < ~850 ps): ship C1 (X=Y=1, Wn=120n/Wp=215n). Done -- no buffering needed.
- C1 misses on the Sum path only: adopt C2 (X=4 on MSI, Y=1). Update the
full_adder.scsSum-final-inverter multipliers to 4x, note it in the header. - C1 misses on Cout only: adopt C3 (X=1, Y=2) -- widen just the carry inverter (less X2 loading than Y=4+).
- C2/C3 still miss: the bottleneck is the sum gate (internal SB node), not the output inverter. Widen the sum PMOS 3-series MS12-14 (6x -> 8-10x) and/or the input buffer PMOS (140n -> larger) before pushing X further. Past X~6 the output inverter is slope-limited; stop.
- If measured >> predicted everywhere: check the model include,
tnom = 27matches the sweep temperature, and that PMOS bulks are atvdd(not gnd -- a common error that strands outputs ~818 mV, below VIH).
What to bring back
For each config and each vector, the four numbers (tpLH/tpHL at C_O and
S). With those we can either accept a sizing or add the slope term to
size_adder.py to close the model-sim gap and re-predict before the next
silicon-real sizing pass.