Skip to content

Session 3: Schematic Design & Simulation

Assignment 1 — NAND Gate with Sky130 PDK

Starting from the AND gate netlist from the previous session, I removed the output inverter to convert it to a NAND gate. Then I replaced the simple transistor models with real Sky130 PDK models.

Copying the AND gate as a starting point:

cp and_gate.sp nand_gate.sp

First draft netlist in nano — with wrong PDK path and M prefix:

nand_gate.sp v1 — Mp1/Mp2/Mn1/Mn2, wrong sky130A path

Problems encountered

Problem 1: Wrong PDK path

The path in the course notes did not exist in our environment. Running ngspice gave a model error:

ngspice run command

ngspice startup then model error — "could not find a valid modelname"

I found the correct path using:

find /foss/pdks -name "sky130.lib.spice" 2>/dev/null

find command output — correct PDK path

Correct path:

/foss/pdks/ciel/sky130/versions/54435919abffb937387ec956209f9cf5fd2dfbee/sky130A/libs.tech/ngspice/sky130.lib.spice

Problem 2: Transistor naming convention

Even after fixing the path, the same error persisted:

nand_gate.sp v2 — correct path but still M prefix

ngspice error again — "could not find a valid modelname" on Mp1

Sky130 PDK transistors are subcircuits, not primitive models, so they require the X prefix instead of M. Mp1Xp1, Mn1Xn1. This is not obvious from the documentation — it only becomes clear when ngspice throws this error.

Changes from AND gate to NAND gate

  • Removed inverter transistors Mp3 and Mn3
  • Changed Mp1, Mp2Xp1, Xp2 with Sky130 PMOS model
  • Changed Mn1, Mn2Xn1, Xn2 with Sky130 NMOS model
  • Updated .lib path to correct PDK location
  • Added propagation delay measurements

Final netlist:

* NAND Gate using CMOS with Sky130 PDK
Vdd vdd gnd 1.8
VinA inA gnd PULSE(0 1.8 1n 100p 100p 2n 6n)
VinB inB gnd PULSE(0 1.8 1n 100p 100p 4n 10n)
Xp1 nand_out inA vdd vdd sky130_fd_pr__pfet_01v8 W=0.99 L=0.150
Xp2 nand_out inB vdd vdd sky130_fd_pr__pfet_01v8 W=0.99 L=0.150
Xn1 nand_out inA mid gnd sky130_fd_pr__nfet_01v8 W=0.495 L=0.150
Xn2 mid inB gnd gnd sky130_fd_pr__nfet_01v8 W=0.495 L=0.150
.lib "...sky130.lib.spice" tt
.tran 10p 20n
.control
run
plot v(inA) v(inB) v(nand_out)
.endc
.end

Assignment 2 — Simulation & Truth Table Verification

I ran a transient simulation to verify the truth table and measure propagation delays. The two pulse inputs have different frequencies so all 4 input combinations appear over time.

A B NAND Output
0 0 1
0 1 1
1 0 1
1 1 0

The waveform confirms this — nand_out goes LOW only when both inputs are HIGH simultaneously.

ngspice NAND simulation — waveform showing correct NAND behavior, plus tpd measurements

Propagation delay results

tpd_hl and tpd_lh results from ngspice

Measurement Value Direction
tpd_hl 103.6 ps Output HIGH → LOW
tpd_lh 82.5 ps Output LOW → HIGH

The LOW-to-HIGH transition is faster because PMOS transistors pull up quickly when both inputs go low.


Assignment 3 — Initial Analog Block for RGB Mixer

For my chip project I chose to build an RGB Mixer — a chip that takes 3 rotary encoder inputs and controls the brightness of Red, Green, and Blue LEDs independently using PWM signals. Inspired by the silicon-proven project on Tiny Tapeout (tt05, Matt Venn).

As a first analog block, I wrote a voltage amplifier in Verilog-A. This block takes an input voltage and multiplies it by a gain parameter. For the RGB Mixer, this represents the signal conditioning stage — scaling the encoder voltage before feeding it into the PWM generator.

Writing rgb_pwm.va in nano:

nano — rgb_pwm.va Verilog-A code with gain=1.5

Compiling with openvaf:

openvaf rgb_pwm.va command

openvaf — "Finished building rgb_pwm.va in 0.26s"

Writing the SPICE testbench:

nano rgb_pwm_test.sp command

nano — rgb_pwm_test.sp netlist

Running the simulation:

ngspice rgb_pwm_test.sp command

Verilog-A amplifier simulation — v(in) red 1V, v(out) blue 1.5V, gain=1.5 confirmed

A 1V amplitude input (red) produces a 1.5V amplitude output (blue) — confirming gain=1.5 is working correctly.


What I learned

The X prefix rule for Sky130 subcircuits is not obvious from the documentation — it only becomes clear when ngspice throws a model error. This is the kind of thing that is easy to miss and hard to debug without knowing what to look for.

Propagation delay depends on direction. tpd_hl and tpd_lh are different because PMOS and NMOS transistors have different drive strengths. In Sky130 this asymmetry is intentional — PMOS transistors are sized wider to compensate for their lower mobility.

Verilog-A allows you to describe analog behavior mathematically and compile it into a model that ngspice can use. The openvaf compiler translates the .va file into an .osdi shared library that ngspice loads at runtime with pre_osdi.