The objective of this tutorial is to describe how Spectre simulations can be done at the command line, as a quick alternative to launching the Cadence Virtuoso GUI. This tutorial will go through a simple RLC Butterworth filter AC simulation. More examples will be added in the future.

Preliminaries

A Spectre simulation needs an input file, or files, that contains your circuit information, model information, and analysis information, and device model files for transistors, diodes, etc.

Input File

A sample input file (butterworth.scs) describing the RLC implementation of a 5th order low-pass Butterworth filter.

// Butterworth test filter

simulator lang=spectre
global 0

R1 (vout 0) resistor r=1
R0 (vin net1) resistor r=1
C2 (net2 0) capacitor c=31.83n
C1 (vout 0) capacitor c=9.836n
C0 (net1 0) capacitor c=9.836n
L1 (net2 vout) inductor l=25.75n
L0 (net1 net2) inductor l=25.75n
V0 (vin 0) vsource mag=1 type=dc
simulatorOptions options reltol=1e-3 vabstol=1e-6 iabstol=1e-12 temp=27 \
    tnom=27 scalem=1.0 scale=1.0 gmin=1e-12 rforce=1 maxnotes=5 maxwarns=5 \
    digits=5 cols=80 pivrel=1e-3 sensfile="../psf/sens.output" \
    checklimitdest=psf 

ac ac start=1M stop=100M dec=1000 annotate=status 
saveOptions options save=allpub subcktprobelvl=2

// end of file

In this example, since we are just using generic resistors, capacitors, and inductors, we will not need a model file.

Invoking Spectre via the command line

Before using Spectre, make sure your environment variables are setup. This includes the PATH variable that includes the location of the Spectre executable, as well as the CDS_LICENSE_FILE variable that points to the appropriate license server. Calling Spectre via the command line with the ‘-V’ option will return the version:

spectre -V

Invoking Spectre via the command line to run the AC simulation specified in the butterworth.scs file would look like:

spectre -64 butterworth.scs +escchars +log ./butterworth/psf/spectre.out \
-format psfascii -raw ./butterworth/psf +lqtimeout 900 -maxw 5 -maxn 5 \
+logstatus

After running, Spectre will create the directory ./butterworth, with the log file ./butterworth/psf/spectre.out. The AC analysis output is ./butterworth/psf/ac.ac, in PSF-ascii format. This output text file can then be post-processed manually or via scripts.

Sample post-processing script

Below is a simple Python script that runs the Spectre simulator, then reads the output file. The ac analysis output file is then processed to determine the 3-dB bandwidth and attenuation of the filter.

# filename: Spectre_run.py
# sample usage: 
# python Spectre_run.py butterworth.scs
#

import sys
import os.path
import subprocess
import shlex
import numpy as np
from si_prefix import si_format

infile = str(sys.argv[1])
infile_noext = os.path.splitext(infile)[0]

spectre_args = ["spectre -64",
        infile,
        "+escchars",
        "=log ./" + infile_noext + "/psf/spectre.out",
        "-format psfascii",
        "-raw ./" + infile_noext + "/psf",
        "+lqtimeout 900",
        "-maxw 5",
        "-maxn 5",
        "+logstatus"]

run_string = ""
for i in spectre_args:
    run_string = run_string + i + " "
  
run_command = shlex.split(run_string)
print("Running simulation...")
subprocess.call(run_command) 
print("Done.")

output_file = "./" + infile_noext + "/psf/ac.ac"

header = 1
freq = []

vout_mag = []
vout_phase = []

with open(output_file, 'rU') as f:
    for line_terminated in f:
        # read simulation output line by line
        # assumed to be 'psfascii'
        line = line_terminated.rstrip('\n')
        
        if line == "VALUE":     # simulation data starts here
            header = 0
            
        if header == 0:
            # remove the parenthesis in the data
            line1 = line.replace('(', '')
            line2 = line1.replace(')', '')
            
            # split the data into strings if a space is encountered
            line_val = shlex.split(line2)
            
            # find frequency values
            if line_val[0] == "freq":
                freq.append(float(line_val[1]))
                
            # find the output voltage values
            # this is the name you chose in the netlist
            # simulation data is formatted as (real, imaginary)
            if line_val[0] == "vout":
                vout = float(line_val[1]) + (1j * float(line_val[2]))
                mag = np.absolute(vout)
                phase = np.angle(vout)
                vout_mag.append(mag)
                vout_phase.append(phase)

# check 3dB specifications
for i in range(len(freq)):
    droop_dB = 20*np.log10(vout_mag[0]) - 20*np.log10(vout_mag[i])
    if droop_dB > 3:
        idx_3dB = i
        break
    
fstop = 20e6
# check attenuation specifications
for i in range(len(freq)):
    if freq[i] > fstop:
        idx_fstop = i     
        break
    
attenuation_dB = 20*np.log10(vout_mag[0]) - 20*np.log10(vout_mag[idx_fstop])
att_dB = "{0:.2f}".format(attenuation_dB)
f3dB = si_format(freq[idx_3dB], precision=2)
fstop_form = si_format(fstop, precision=2)

print("")
print("Simulation Summary:")
print("3-dB Frequency is " + str(f3dB) + "Hz")
print("Attenuation at " + str(fstop_form) + "Hz is " + str(att_dB) + " dB")

# end of file

You can also use the freq, vout_mag, and vout_phase arrays to generate the frequency response plots.