Whenever I perform a noise analysis on a circuit, I like to verify the result with a simulation. I have really enjoyed Eldo’s transient noise analysis in the past, but currently I only have access to HSPICE. All the spice simulators have AC noise analysis features. Here is a toy example that I use to verify basic functionality prior to getting more complicated.
Suppose you have a single NMOS transistor configured as follows where the drain is active loaded with an ideal current source and the gate is biased with an ideal voltage source.

The goal here is to calculate the input referred noise of this amplifier and verify the result with an AC noise simulation in HSPICE. The HSPICE netlist I use to simulate with is:
* HSPICE Netlist
* Include models
.lib '../../../pdk/hspicemm/cmn018_assp_v1d1.l' TT
.lib '../../../pdk/hspicemm/cmn018_assp_v1d1.l' TT_3V
.GLOBAL gnd!
m0 vd vg gnd! gnd! nch3 w='0.42u' l='0.35u' nf=1 m=1 ad='0.2016p' as='0.2016p'
+ pd='1.38u' ps='1.38u' nrd=1.14286 nrs=1.14286 sa='0.48u' sb='0.48u' sd=0
v4 vg gnd! dc=.9 ac=1
i3 gnd! vd dc='3u'
.options POST=1 parhier=local
.TEMP 45
.ac DEC 10 1000 100G
.noise V(vd) V4
.probe noise onoise onoise(m) onoise(db)
.probe noise inoise inoise(m) inoise(db)
.probe GMO(M0)
.probe GDSO(M0)
.probe CDDBM(M0)
.end
Because the transistor models are proprietary to our manufacturer, I do not include them here. They are level=54 (BSIM 4) transistors, whose parameter descriptions can be found at this web address: http://www-device.eecs.berkeley.edu/~bsim3/bsim4_get.html. For the purposes of verifying these against hand calculations, I temporarily disabled flicker noise in the models by setting the NOIA, NOIB, NOIC parameters to 0. BSIM 4.0 models (see page 87-88 of BSIM manual) use a more complicated equation for thermal noise than the traditional thermal noise = 8/3kTgm equation appropriate for hand analysis, but as you will see, the hand analysis is pretty close.
After running this simulation, I first look at the AC response and extract the gain and bandwidth from the gm, gds, and Cdd parameters that I probed in the simulation. As the plot below shows, calculating the gain as A=gm/gds and the single pole as tau=Cdd/gds gives a first order model that matches the simulated results quite well until the a zero from the Cgd hits above 10 GHz.

From this first order model, I calculate the output referred noise spectral density as N=8/3kTgm/gds^2. This is then filtered by the same first order model (but without the gain) to produce a frequency response as shown below in green. The result from the HSPICE simulation matches to within 2dB. This discrepancy comes from the more complex noise model employed by HSPICE and BSIM 4. After integrating the PSD, spice reports the output noise as 18.2mV and I calculate 14.9mV. Not bad for a hand calculation. The input referred noise, on the other hand, is somewhat of a mystery to me. Rather than dividing by the DC gain of the circuit to input refer the noise, HSPICE divides the output PSD by the frequency response of the system, and the result is spice’s input referred noise becomes quite broadband—almost white (see red line in plot below). Integrating this to get the equivalent total input referred noise is useless. So I calculate the input referred noise by dividing the output RMS noise by the gain because the number reported by HSPICE for the input referred total noise is meaningless. Since the gain is 40 in this example, the input referred noise is 375uV.

The code I wrote to generate these plots utilizes all open source software including Numerical Python and Matplotlib, which I highly recommend as an alternative to Matlab. I modified Mike Perrott’s HSPICE Matlab Toolbox code to read hspice sim data in as a numpy array. The code follows:
from cad.sim import hspice, anal
import pylab, numpy
s,p = hspice.read("results/netlist.ac0")
f=s["HERTZ"]
gm = s["gmo(m0"][0]
gds= s["gdso(m0"][0]
C = s["cddbm(m0"][0]
A = gm/gds
tau = C/gds
F = 1/numpy.sqrt(1+(2*numpy.pi*f*tau)**2)
H = A*F
def dB(x):
return 20*numpy.log10(numpy.abs(x))
pylab.figure()
pylab.semilogx(f, dB(s["vd"]), label="Simulation Result", lw=2)
pylab.semilogx(f, dB(H), label="First Order Model", lw=2)
pylab.grid(True)
pylab.figtext(0.2,0.4,"A=%2.1f f=%2.1fMHz" % (A, 1/tau/2/numpy.pi/1e6), bbox=dict(edgecolor="k", facecolor="w"))
pylab.xlabel("Frequency (Hz)")
pylab.ylabel("Magnitude (dB)")
pylab.title("Transfer function from 'vg' to 'vd'")
pylab.legend(loc=3)
pylab.savefig("transfer.png")
kT = 1.38e-23 * 315; # Joules
onoise = 8/3.* kT * gm / (gds**2)
inoise = 8/3.* kT / gm
nb = 1/4./tau
sigma = numpy.sqrt(inoise * nb)
pylab.figure()
pylab.semilogx(f, s["onoise(db"], label="Simulated output noise (onoise)", lw=2)
pylab.semilogx(f, dB(numpy.sqrt(onoise)*F), label="Hand calculated output noise", lw=2)
pylab.semilogx(f, s["inoise(db"], label="Simulation input noise (inoise)", lw=2)
pylab.grid(True)
pylab.xlabel("Frequency (Hz)")
pylab.ylabel("Noise (dB/sqrt(Hz))")
pylab.title("Noise Power Spectral Density Plots")
pylab.figtext(0.2,0.6,"""Spice Total Output Noise=18.2mV
Hand Calculated Output Noise=%2.1fmV""" % (sigma*A*1e3,), bbox=dict(edgecolor="k", facecolor="w"))
pylab.legend(loc=3)
pylab.savefig("noise.png")
Weak Inversion
The previous example was for a transistor in strong inversion. When I change the operating to bias the above circuit in weak inversion, the spice generated output noise becomes= 27.6mV and my hand calculations now use the noise spectral density equation that 4*gamma*kT*gm where gamma=n/2 and where n is the subthreshold slope ideality factor. Using a ideality factor of 2, my hand calculations give the output referred noise as 26.5mV, which is within 4% of the spice result.