Full adder files using FreeCAD 45nm.
  • Python 60.3%
  • DM 36.7%
  • Java 3%
Find a file
Leo Qi 015a1e735a
balance: add minimal PMOS/NMOS width balancer (balance.py)
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
2026-07-01 22:45:45 -04:00
cadence/Project_1 add full adder files 2026-07-01 18:35:55 -04:00
.envrc.default all: create size_adder.py 2026-06-25 16:48:08 -04:00
.gitignore all: create size_adder.py 2026-06-25 16:48:08 -04:00
AGENTS.md docs: rewrite AGENTS.md with clear objective + current method 2026-07-01 22:45:44 -04:00
balance.py balance: add minimal PMOS/NMOS width balancer (balance.py) 2026-07-01 22:45:45 -04:00
full_adder.scs netlist: add proper worst case delay 2026-06-25 18:09:32 -04:00
monte_carlo.csv add full adder files 2026-07-01 18:35:55 -04:00
propagation_delay.csv add full adder files 2026-07-01 18:35:55 -04:00
README.md docs: update README to period-based 1 GHz spec + current sizing 2026-07-01 22:45:45 -04:00
report.md initial commit 2026-07-01 18:35:55 -04:00
size_adder.py size: account for input double buffer + finite-slew propagation 2026-06-26 00:14:46 +00:00

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)

  1. Models. include the same PDK model file used for the pygmid sweep (the nch/pch BSIM4 models). Set tnom = 27.
  2. Supply. vdd net = 1.1 V via an ideal vdc. gnd = 0.
  3. Inputs A, B, C_I. Three vpulse/vpwl sources, 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.
  4. Loads. C_LOAD = 10 fF from C_O to gnd and from S to gnd (both outputs, per spec).
  5. Analysis. tran tran step=1p stop=5n icnode=all (or whatever your kit needs for clean 50% crossings). Optionally noise off, reltol tight (1e-5) so delay measurements are stable.
  6. Measurements. cross/delay statements for 50% points:
    • tpLH(C_O), tpHL(C_O) vs the toggling input that causes the transition
    • tpLH(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):

  1. Cout rising (A->Cout): set C_I=0, B=0, pulse A 0->1.1->0. Cout = A (since B=Ci=0) -> measures tpLH(C_O) and tpHL(C_O).
  2. Cout via carry chain: A=1, B=0, pulse C_I 0->1.1. Cout = Ci, exercises the M6/M7 stack -> tpLH(C_O).
  3. Sum via X2 (worst, 3-stack + carry): A=1, B=1, pulse C_I 0->1.1. Cout rises (1) and Sum falls through the sum PDN 3-stack driven by the carry-ctrl -> tpHL(S). This is the predicted worst path.
  4. Sum direct (bypass X2): C_I=0, pulse A (and/or B) 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.scs Sum-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 = 27 matches the sweep temperature, and that PMOS bulks are at vdd (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.