Sky130nm tutorial
If you find an error in what I’ve made, then fork, fix lectures/lr0_tools.md, commit, push and create a pull request. That way, we use the global brain power most efficiently, and avoid multiple humans spending time on discovering the same error.
- Tools
- Check that magic and xschem works
- Design tutorial
- Create the IP
- The file structure
- Github setup
- Start working
- Draw Schematic
- Typical corner SPICE simulation
- All corners SPICE simulations
- Draw Layout
- Layout verification
- Extract layout parasitics
- Simulate with layout parasitics
- Make documentation
- Edit info.yaml
- Setup github pages
- Frequency asked questions
Tools
I would strongly recommend that you install all tools locally on your system.
For the analog toolchain we need some tools, and a process design kit (PDK).
- Skywater 130nm PDK. I use open_pdks to install the PDK
- Magic VLSI for layout
- ngspice for simulation
- netgen for LVS
- xschem
- python > 3.10
The tools are not that big, but the PDK is huge, so you need to have about 50 GB disk space available.
Setup WSL (Applicable for Windows users)
Install a Linux distribution such as Ubuntu 24.04 LTS by running the following command in PowerShell on Windows and follow the instructions.
wsl --install -d Ubuntu-24.04
When you have installed the Linux distribution and signed into it, install make
sudo apt install make
Setup public key towards github
Do
ssh-keygen -t rsa
And press “enter” on most things, or if you’re paranoid, add a passphrase
Then
cat ~/.ssh/id_rsa.pub
And add the public key to your github account. Settings - SSH and GPG keys
Get AICEX and setup your shell
You don’t have to put aicex in $HOME/pro
, but if you don’t know where to put
it, chose that directory.
cd
mkdir pro
cd pro
git clone --recursive https://github.com/wulffern/aicex.git
You need to add the following to your ~/.bashrc
(note that ~
refers to your
home directory $HOME/.bashrc
also works, or $HOME/.bash_profile
on some
newer macs)
export PDK_ROOT=/opt/pdk/share/pdk
export LD_LIBRARY_PATH=/opt/eda/lib
export PATH=/opt/eda/bin:$HOME/.local/bin:$PATH
On systems with python3 > 3.12
On newer systems it’s not trivial to install python packages because python is externally managed. As such, we need to install a python environment.
#- Find a package similar to name below
sudo apt install python3.12-venv
sudo mkdir /opt/eda/python3
sudo chown -R $USER:$USER /opt/eda/python3/
python3 -m venv /opt/eda/python3
Modify the ~/.bashrc
to include the python environment
export PATH=/opt/eda/bin:/opt/eda/python3/bin:$HOME/.local/bin:$PATH
Install Tools
Make sure you load the settings before you proceed
source ~/.bashrc
Hopefully the commands below work, if not, then try again, or try to understand what fails. There is no point in continuing if one command fails.
cd aicex/tests/
make requirements
make tt
make eda_compile
sudo make eda_install
python3 -m pip install matplotlib numpy click svgwrite pyyaml pandas tabulate wheel setuptools tikzplotlib
source install_open_pdk.sh
cd ../..
Install cicconf
cIcConf is used for configuration. How the IPs are connected, and what version of IPs to get.
cd aicex/ip/cicconf
git checkout main
git pull
python3 -m pip install -e .
cd ../
Update IPs
cicconf update
cd ..
Install cicsim
cIcSim is used for simulation orchestration.
cd aicex/ip/cicsim
python3 -m pip install -e .
cd ../..
Setup your ngspice settings
Edit ~/.spiceinit
and add
set ngbehavior=hsa ; set compatibility for reading PDK libs
set ng_nomodcheck ; don't check the model parameters
set num_threads=8 ; CPU hardware threads available
set skywaterpdk
option noinit ; don't print operating point data
option klu
optran 0 0 0 100p 2n 0 ; don't use dc operating point, but transient op
option opts
Check that magic and xschem works
To check that magic and xschem works
cd ~/pro/aicex/ip/sun_sar9b_sky130nm/work
magic ../design/SUN_SAR9B_SKY130NM/SUNSAR_SAR9B_CV.mag &
xschem -b ../design/SUN_SAR9B_SKY130NM/SUNSAR_SAR9B_CV.sch &
Design tutorial
Create the IP
I’ve made some scripts to automatically generate the IP.
To see what files are generated, see tech_sky130A/cicconf/ip_template.yaml
cd aicex/ip
cicconf newip ex
The file structure
It matters how you name files, and store files. I would be surprised if you had a good method already, as such, I won’t allow you to make your own folder structure and names for things. I also control the filenames and folder structure because there are many scripts to make your life easier (yes, really) that rely on an exact structure. Don’t mess with it.
Github workflows
On github it’s possible use something called workflows to run things every time you push a new version. It’s really nice, since it can then do checks that you’re design is valid.
The grading of the milestones is determined by passing github workflows.
We will also check that you have not cheated, and modified the workflows just to get them passing.
The workflows are defined below.
.github
workflows
docs.yaml # Generate a github page
drc.yaml # Run Design Rule Checks
gds.yaml # Generate a GDS file from layout
lvs.yaml # Run Layout Versus Schematic and Layout Parasitic Extraction
sim.yaml # Run a simulation
Configuration files
Each IP has a few files that define the setup, you’ll need to modify at least
the README.md
and the info.yaml
.
.gitignore # files that are ignored by git
README.md # Frontpage documentation
config.yaml # What libraries are used. This file can be used by cicconf
info.yaml # Setup names, authors etc
media # Where you should store images for documentation
tech -> ../tech_sky130A # The technology library
Design files
A “cell” in the open source EDA world should consists of the following files
- Schematic (.sch)
- Layout (.mag)
- Documenation (.md)
The files must have the same name, and must be stored in design/<LIB>/
as
shown below.
Note there are also two symbolic links to other libraries. These two libraries contain standard cells and standard analog transistors (ATR) that you should be using.
design
JNW_EX_SKY130A
JNW_EX.sch
JNW_ATR_SKY130A -> ../../jnw_atr_sky130a/design/JNW_ATR_SKY130A
JNW_TR_SKY130A -> ../../jnw_tr_sky130a/design/JNW_TR_SKY130A
For example, if the cell name was JNW_EX
, then you would have
design/JNW_EX_SKY130A/JNW_EX.sch
: Schematic (xschem)design/JNW_EX_SKY130A/JNW_EX.sym
: Schematic (xschem)design/JNW_EX_SKY130A/JNW_EX.mag
: Layout (Magic)design/JNW_EX_SKY130A/JNW_EX.md
: Markdown documentation (any text editor)
All these files are text files, so you can edit them in a text editor, but mostly you shouldn’t (except for the Markdown)
Simulations
All simulations shall be stored in sim
. Once you have a Schematic ready for
simulation, then
cd sim
make cell CELL=JNW_EX
This will make a simulation folder for you. Repeat for all your cells.
sim
Makefile
cicsim.yaml -> ../tech/cicsim/cicsim.yaml
The work
All commands (except for simulation), shall be run in the work
folder.
In the work/
folder there are startup files for Xschem (xschemrc) and Magic (.magicrc).
They tell the tools where to find the process design kit, symbols, etc. At some point you probably
need to learn those also, but I’d wait until you feel a bit more comfortable.
work
.magicrc
Makefile
mos.24bit.dstyle -> ../tech/magic/mos.24bit.dstyle
mos.24bit.std.cmap -> ../tech/magic/mos.24bit.std.cmap
xschemrc
Github setup
Create a repository on github
cd jnw_ex_sky130nm
git remote add origin \
git@github.com:<your user name>/jnw_ex_sky130nm.git
Start working
Edit README.md
Open README.md in your favorite text editor and make necessary changes.
Familiarize yourself with the Makefile and make
I write all commands I do into a Makefile. There is nothing special with a Makefile, it’s just what I choose to use 20 years ago. I’m not sure I’d choose something different now.
cd work
make
Take a look inside the file called Makefile.
Draw Schematic
The block we’ll make is a current mirror with a 1 to 5 scaling.
A schematic is how we describe the connectivity, and the types of devices in an analog circuit. The open source schematic editor we will use is XSchem.
Open the schematic:
xschem -b ../design/JNW_EX_SKY130A/JNW_EX.sch &
Add Ports
Add IBPS_5U and IBNS_20U ports, the P and N in the name signifies what transistor the current comes from. So IBPS must go into a diode connected NMOS, and N will be our output, and go into a diode connected PMOS somewhere else.
Add transistors
Use ‘Shift-I’ to open the library manager. Click the jnw_ex0_sky130A/design
path, then JNW_ATR_SKY130A
and select JNW_ATR_4C5F0.sym
The naming convention for these transistors is <number of contacts on
drain/source>C<times minimum gate length>F
, so the before the C is the width,
and before/after the F is the length. The absolute size does not matter for now.
Just think “4C5F0 is a 4 contact wide long transistor”, while a “4C1F2 is a 4
contact wide, short transistor”.
Select the transistor and press ‘c’ to copy it, while dragging, press ‘shift-f’ to flip the transistor so our current mirror looks nice. ‘shift-r’ rotates the transistor, but we don’t want that now.
Press ESC to deselect everything
Select the input transistor, and change the name to ‘xi’
Select the output transistor, and change the name to ‘xo[3:0]’. Using bus notation on the name will create 4 transistors
Select ports, and use ‘m’ to move the ports close to the transistors.
Press ‘w’ to route wires.
Use ‘shift-z’ and z, to zoom in and out
Use ‘f’ to zoom full screen
Remember to save the schematic
Netlist schematic
Check that the netlist looks OK
In work/
make xsch CELL=JNW_EX
cat xsch/JNW_EX.spice
Typical corner SPICE simulation
I’ve made cicsim that I use to run simulations (ngspice) and extract results
Setup simulation environment
Navigate to the jnw\_ex0\_sky130nm/sim/
directory.
Make a new simulation folder
cicsim simcell JNW_EX_SKY130A JNW_EX ../tech/cicsim/cell_spice/template.yaml
I would recommend you have a look at simcell_template.yaml file to understand what happens.
Familiarize yourself with the simulation folder
I’ve added quite a few options to cicsim, and it might be confusing. For reference, these are what the files are used for
File | Description |
---|---|
Makefile | Simulation commands |
cicsim.yaml | Setup for cicsim |
summary.yaml | Generate a README with simulation results |
tran.meas | Measurement to be done after simulation |
tran.py | Optional python script to run for each simulation |
tran.spi | Transient testbench |
tran.yaml | What measurements to summarize |
The default setup should run, so
cd JNW_EX
make typical
Modify default testbench (tran.spi)
Delete the VDD source
Add a current source of 5uA, and a voltage source of 1V to IBNS_20U
IBP 0 IBPS_5U dc 5u
V0 IBNS_20U 0 dc 1
Save the current in V0 by adding i(V0) to the save statement in the testbench
Save the voltage by adding v(IBPS_5U) to the save statement
.save i(V0) v(IBPS_5U)
Modify measurements (tran.meas)
Add measurement of the current and VGS. It must be added between the “MEAS_START” and “MEAS_END” lines.
let ibn = -i(v0)
meas tran ibns_20u find ibn at=5n
meas tran vgs_m1 find v(ibps_5u) at=5n
Run simulation
make typical
and check that the output looks okish.
Try to run the simulation again
make typical
If everything works, then the simulation now should not be run. Every time
cicsim runs (provided the sha: True
option is set in cicsim.yaml
) cicsim will
compute a SHA hash of all files (stored in output_tran/.sha) that is
referenced in the tran.spi
. Next time cicsim is run, it checks the hash’s and
does not re-run if there is no need (no files changed).
Sometimes you want to force running, and you can do that by
make typical OPT="--no-sha"
Often, it’s the measurement that I get wrong, so instead of rerunning simulation every time I’ve added a “–no-run” option to cicsim. For example
make typical OPT="--no-run"
will skip the simulation, and rerun only the measurement. This is why you should split the testbench and the measurement. Simulations can run for days, but measurement takes seconds.
Modify result specification (tran.yaml)
Add the result specifications, for example
ibn:
src:
- ibns_20u
name: Output current
min: -20%
typ: 20
max: 20%
scale: 1e6
digits: 3
unit: uA
vgs:
src:
- vgs_m1
name: Gate-Source voltage
typ: 0.6
min: 0.3
max: 0.7
scale: 1
digits: 3
unit: V
Re-run the measurement and result generation
make typical OPT="--no-run"
Open result/tran_Sch_typical.html
Check waveforms
You can either use ngspice, or you can use cicsim, or you can use something I don’t know about
Open the raw file with
cicsim wave output_tran/tran_SchGtKttTtVt.raw
Load the results, and try to look at the plots. There might not be that much interesting happening
All corners SPICE simulations
Analog circuits must be simulated for all physical conditions, we call them corners. We must check high and low temperature, high and low voltage, all process corners, and device-to-device mismatch.
For the current mirror we don’t need to vary voltage, since we don’t have a VDD.
Remove Vh and Vl corners (Makefile)
Open Makefile in your favorite text editor.
Change all instances of “Vt,Vl,Vh” and “Vl,Vh” to Vt
Run all corners
To simulate all corners do
make typical etc mc
where etc is extreme test condition and mc is monte-carlo.
Wait for simulations to complete.
Get creative with python
Open tran.py
in your favorite editor, try to read and understand it.
The name
parameter is the corner currently running, for example tran_SchGtAmcttTtVt
.
The measured outputs from ngspice will be added to tran_SchGtAmcttTtVt.yaml
Delete the “return” line.
Add the following lines (they automatically plot the current and gate voltage)
import cicsim as cs
fname = name +".png"
print(f"Saving {fname}")
cs.rawplot(name + ".raw","time","v(ibps_5u),i(v0)",ptype="",fname=fname)
Re-run measurements to check the python code
make typical etc mc OPT="--no-run"
You’ll see that cicsim writes all the png’s. Check with ls -l output_tran/*.png
.
You’ll also notice it will slow down the simulation, so maybe remove the lines
from tran.py
again ;-)
Generate simulation summary
Run
make summary
Install pandoc if you don’t have it
Run
pandoc -s -t slidy README.md -o README.html
to generate a HTML slideshow that you can open in browser. Open the HTML file.
Viewing results without GUI browser
If your on a system without a browser, or indeed a GUI, then it’s possible to view the results in the terminal.
Check if lynx
is installed, if it’s not installed, then
On linux
sudo apt-get install lynx
On Mac
brew install lynx
Then
lynx README.html
Think about the results
From the corner and mismatch simulation, we can observe a few things.
- The typical value is not 20 uA. This is likely because we have a M2 VDS of 1 V, which is not the same as the VDS of M1. As such, the current will not be the same.
- The statistics from 30 corners show that when we add or subtract 3 standard deviation from the mean, the resulting current is outside our specification of +- 20 %. I’ll leave it up to you to fix it.
Draw Layout
A foundry (the factory that makes integrated circuits) needs to know how we want them to create our circuit. So we need to provide them with a “layout”, the recipe, or instruction, for how to make the circuit. Although the layout contains the same components as the schematic, the layout contains the physical locations, and how to actually instruct the foundry on how to make the transistors we want.
Open Magic VLSI
cd work
magic ../design/JNW_EX_SKY130A/JNW_EX.mag
Now brace yourself, Magic VLSI was created in the 1980’s. For it’s time it was extremely modern, however, today it seems dated. However, it is free, so we use it.
Magic VLSI
Try google for most questions, and there are youtube videos that give an intro.
Default magic start with the BOX tool. Mouse left-click to select bottom corner, left-click to select top corner.
Press “space” to select another tool (WIRING, NETLIST, PICK).
Type “macro help” in the command window to see all shortcuts
Hotkey | Function |
---|---|
v | View all |
shift-z | zoom out |
z | zoom in |
x | look inside box (expand) |
shift-x | don’t look inside box (unexpand) |
u | undo |
d | delete |
s | select |
Shift-Up | Move cell up |
Shift-Down | Move cell down |
Shift-Left | Move cell left |
Shift-Right | Moce cell right |
Add transistors
Open Cell -> Place Instance. Navigate to the right transistor.
Place it. Hover over the transistor and select it with ‘s’. Now comes a bit of tedious thing. Select again, and copy. It’s possible to align the transistors on-top of eachother, but it’s a bit finicky.
Place all transistors on top of each other.
Add Ground
In the command window, type
see no *
see viali
see locali
see m1
see via1
see m2
Change to the ‘wire tool’ with spacebar. Press the top transistor ‘S’ and draw all the way down.
Change grid to 0.5 um.
Select a 0.5 um box below the transistors and paint the rectangle (middle click on locali)
Connect guard rings to ground. Use the ‘wire tool’
Connect the sources to ground. Use the ‘wire tool’. Use ‘shift-right click’ to change layer down
Route Gates
Press “space” to enter wire mode. Left click to start a wire, and right click to end the wire.
The drain of M1 transistor needs a connection to from gate to drain. We do that for the middle transistor.
Start the route, press ‘shift-left click’ to go up one layer, route over to drain, and ‘shift-right click’ to go down.
Drain of M2
Use the wire tool to draw connections for the drains.
To add vias you can do “shift-left click” to move up a metal, and “shift-right click” to go down.
Add labels
Select a box on a metal, and use “Edit->Text” to add labels for the ports. Select the port button.
Layout verification
The DRC can be seen directly in Magic VLSI as you draw.
To check layout versus schematic navigate to work/ and do
make cdl lvs
If you’ve routed correctly, then the LVS should be correct.
Extract layout parasitics
With the layout complete, we can extract parasitic capacitance.
make lpe
Check the generated netlist
cat lpe/JNW_EX_lpe.spi
Simulate with layout parasitics
Navigate to sim/JNW_EX. We now want to simulate the layout.
The default tran.spi
should already have support for that.
Open the Makefile, and change
VIEW=Sch
to
VIEW=Lay
Typical simuation
Run
make typical
Corners
Navigate to sim/JNW_EX. Run all corners again
make all
Simulation summary
Open summary.yaml
and add the layout files.
- name: Lay_typ
src: results/tran_Lay_typical
method: typical
- name: Lay_etc
src: results/tran_Lay_etc
method: minmax
- name: Lay_3std
src: results/tran_Lay_mc
method: 3std
Run summary again
make summary
pandoc -s -t slidy README.md -o README.html
Open the README.html and have a look a the results. The layout should be close to the schematic simulation.
Make documentation
Make a file (or it may exists) design/JNW_EX_SKY130A/JNW_EX.md
and add some
docs.
Edit info.yaml
Finally, let’s setup the info.yaml
so that all the github workflows run
correctly.
Mine will look like this.
You need to setup the url (probably something like <your username>.github.io
)
to what is correct for you.
I’ve added the doc section such that the workflows will generate the docs.
The sim is to run a typical simulation.
library: JNW_EX_SKY130A
cell: JNW_EX
author: Carsten Wulff
github: wulffern
tagline: The answer is 42
email: carsten@wulff.no
url: analogicus.github.io
doc:
libraries:
JNW_EX_SKY130A:
- JNW_EX
sim:
JNW_EX: make typical
Setup github pages
Go to github. Press Settings. Press Pages. Choose Build and Deployment -> GitHub Actions
Wait for the workflows to build. And check your github pages. Mine is [https://analogicus.github.io/jnw_ex0_sky130a/](https://analogicus.github.io/jnw_ex0_sky130a/**
Frequency asked questions
My GDS/LVS/DRC action fails, even though it works locally.
Sometimes the reference to the transistors in the magic file might be wrong. Open the .mag file in a text editor and check. The correct way is
use JNWATR_NCH_4C5F0 JNWATR_NCH_4C5F0_0 ../JNW_ATR_SKY130A
It’s the last ../JNW_ATR_SKY130A
that sometimes is missing.