Distroless Containers

Container sizes for applications are often a concern. I recently came across distroless from Google that seems to solve this pain-point while also reducing the potential vulnerability surface of the container as well. So I was looking for a suitable target to try this on.

I use MailHog for testing emails in dev. The published container is unfortunately too old. So seemed like a good opportunity to try distroless. I really wanted to go with a the static base image, but unfortunately ran into issues with CGO_ENABLED=0 for build. For now, the base build will be fine. Here’s the Dockerfile for generating a distroless image of MailHog

FROM golang:1 as builder
# Install MailHog:
RUN apt install git \
&& mkdir -p /root/gocode \
&& export GOPATH=/root/gocode \
&& go install github.com/mailhog/MailHog@latest
FROM gcr.io/distroless/base-debian11:nonroot
COPY –from=builder /root/gocode/bin/MailHog /usr/local/bin/
USER nonroot
WORKDIR /home/nonroot
# Expose the SMTP and HTTP ports:
EXPOSE 1025 8025
view raw Dockerfile hosted with ❤ by GitHub

Integrated Capacitors

Capacitors are important in realizing most circuits.

A capacitor stores energy in an electric field between two “plates”. The basic equation for a capacitor is C=\epsilon\frac{A}{d}. As with most integrated devices, there are trade-offs between the desired aspects of the device and the undesired elements. Many different kinds of capacitors are available that make different trade-offs. The important properties we want in capacitors are:

  1. Cost
  2. Capacitor density: capacitance per unit area
  3. parasitic capacitance: capacitance to other nodes besides the two plates we are interested in
  4. parasitic resistance
  5. parasitic inductance
  6. leakage: appears as a parallel resistor
  7. Breakdown voltage

Metal-Oxide-Metal (MOM) Capacitors

MOM capacitors are the most straight forward capacitors available in an integrated circuit process. They are especially useful in advanced digital optimized CMOS processes with many layers of interconnects. Several geometries can be used (from lowest to highest density): parallel plate, interdigitated (with or without via stack), rotative, and fractal. Vertical bars or vertical parallel plates usually give the best capacitance density, with slightly different trade-offs for C vs Q (= \frac{1}{\omega RC}).

MOM capacitors can be inexpensive for small values: no extra masks are required. But the low density makes large capacitors quite expensive in silicon area. Modern processes actively try to reduce interconnect parasitics using low-K dielectrics, so cap density trend is probably going lower. Some low-K dielectrics can have low breakdown voltages. The parasitic inductance and resistance will depend on the geometry.

Pros: no extra masks, or process options needed, good linearity

Cons: moderate bottom plate parasitic, low density, high series inductance and resistance, possibly low breakdown voltage,


MIM Capacitors

MIM capacitors are formed by sandwiching a thin dielectric between two metal layers. Typically the dielectric thickness is much smaller than IMD thickness. This requires more masks for the top electrode as well as the dielectric.

Pros: lower bottom plate parasitic, high density, good linearity, low

Cons: extra mask cost (offset by potentially lower area)

Source: A new damascene architecture for high-performance metal–insulator–metal capacitors integration – Scientific Figure on ResearchGate. Available from: https://www.researchgate.net/245128583_fig1_Fig-1-Integration-scheme-of-a-single-damascene-MIM-capacitor-in-the-back-end-of-line [accessed 26 Jun, 2017]


Every MOSFET has a capacitor at it’s heart. All one needs to do is connect the source and drain together to make bottom plate and the gate serves as top plate. This capacitor is available in every process, but suffers from a severe limitation: It’s highly non-linear as the effective gate thickness is dependent on the bias applied. The applied gate voltage can change the concentration of carriers (holes or electrons) in the channel. At some voltage, the channel can be depleted of all carriers and the capacitance reaches a minimum. Voltages higher or lower will collect carriers in the channel and create a capacitor of reasonable value.

C-V (or capacitance-voltage) profile curves
Saumitra Raj Mehrotra; Gerhard Klimeck (2010), “CV profile with different oxide thickness,” https://nanohub.org/resources/8818.

Enhanced MOSCAP on highly-doped diffusion

The obvious way to improve the MOSCAP linearity is to make sure the channel cannot be depleted. This improves the linearity a lot, to about 2-5% total variation. It is usually done with NMOS device because it is naturally in accumulation mode. Adding an extra implant pushes it further into accumulation. One extra mask can help achieve a better trade-off of density and cost.


Depending on the process available, the density, cost and linearity requirements, one can use one or more of MOM, MIM, MOSCAP, or enhanced MOSCAP. MOM is cheap, lowest density, and most linear. MIM cap is the most expensive, good density, and very linear. MOSCAP is cheapest, high density, and very non-linear. Enhanced MOSCAP is cheap, high-density, and somewhat linear.

Xyce on macOS Sierra

Xyce is a great free simulator. I recently tried to fire it up on macOS Sierra and ran into a dependency issue. I want to use the Open MPI version to take advantage of multiple cores. The version of Open MPI installed on my system is at a different path and a different version compared to the one Xyce was compiled with. Fortunately, on macOS there’s an easy work around:

otool -L /usr/local/Xyce-Release-6.5.0-OPENMPI-OPENSOURCE/bin/Xyce
install_name_tool -change \
  /opt/openmpi/x86_64-icc-12.1/1.8.2/lib/libmpi_cxx.1.dylib \
  /usr/local/opt/open-mpi/lib/libmpi_cxx.dylib \
install_name_tool -change \
  /opt/openmpi/x86_64-icc-12.1/1.8.2/lib/libmpi.1.dylib \
  /usr/local/opt/open-mpi/lib/libmpi.dylib \

It seems to run fine now.

gm/Id method

The traditional circuit bias and sizing method using square law, as described in textbooks, is cumbersome and not very accurate in today’s sub-micron technologies. Unfortunately, the prevalent textbooks being rather old, still teach the square law method.

A more modern method using “gm/Id” of the transistor has been known since 1996 (Silviera, et. al. 1996), but hasn’t made its way into many designers’ toolkits. Instead of trying to explain it here, I suggest reading Bernhard E Boser’s (2011) lecture notes.

If you’d like to try this yourself, I have created an example testbench for ngspice using 45nm predictive technology models from ASU and a python script to post-process it.

If you find an error, have an idea for improving it, or just want to discuss this generally, please leave a reply or contact me.

Silveira, F., D. Flandre, and P. G.A Jespers. “A gm/ID Based Methodology for the Design of CMOS Analog Circuits and Its Application to the Synthesis of a Silicon-on-Insulator Micropower OTA.” IEEE Journal of Solid-State Circuits 31, no. 9 (September 1996): 1314–19. doi:10.1109/4.535416.
Boser, Bernhard E. “Analog Design Using gm/Id and Ft Metrics,” 2011. http://www.eecs.berkeley.edu/~boser/presentations/2011-12%20OTA%20gm%20Id.pdf.

ngspice raw and numpy

Numpy makes it almost trivial to read ngspice raw binary files into python:

# MIT license: https://opensource.org/licenses/MIT
# See https://github.com/Isotel/mixedsim/blob/master/python/ngspice_read.py
# for a more complete library. Isotel's version is GPL licensed
from __future__ import division
import numpy as np
BSIZE_SP = 512 # Max size of a line of data; we don't want to read the
# whole file to find a line, in case file does not have
# expected structure.
MDATA_LIST = [b'title', b'date', b'plotname', b'flags', b'no. variables',
b'no. points', b'dimensions', b'command', b'option']
def rawread(fname: str):
"""Read ngspice binary raw files. Return tuple of the data, and the
plot metadata. The dtype of the data contains field names. This is
not very robust yet, and only supports ngspice.
>>> darr, mdata = rawread('test.py')
>>> darr.dtype.names
>>> plot(np.real(darr['frequency']), np.abs(darr['v(out)']))
# Example header of raw file
# Title: rc band pass example circuit
# Date: Sun Feb 21 11:29:14 2016
# Plotname: AC Analysis
# Flags: complex
# No. Variables: 3
# No. Points: 41
# Variables:
# 0 frequency frequency grid=3
# 1 v(out) voltage
# 2 v(in) voltage
# Binary:
fp = open(fname, 'rb')
plot = {}
count = 0
arrs = []
plots = []
while (True):
mdata = fp.readline(BSIZE_SP).split(b':', maxsplit=1)
if len(mdata) == 2:
if mdata[0].lower() in MDATA_LIST:
plot[mdata[0].lower()] = mdata[1].strip()
if mdata[0].lower() == b'variables':
nvars = int(plot[b'no. variables'])
npoints = int(plot[b'no. points'])
plot['varnames'] = []
plot['varunits'] = []
for varn in range(nvars):
varspec = (fp.readline(BSIZE_SP).strip()
assert(varn == int(varspec[0]))
if mdata[0].lower() == b'binary':
rowdtype = np.dtype({'names': plot['varnames'],
'formats': [np.complex_ if b'complex'
in plot[b'flags']
else np.float_]*nvars})
# We should have all the metadata by now
arrs.append(np.fromfile(fp, dtype=rowdtype, count=npoints))
fp.readline() # Read to the end of line
return (arrs, plots)
if __name__ == '__main__':
arrs, plots = rawread('test.raw')
# Local Variables:
# mode: python
# End:
view raw rawread.py hosted with ❤ by GitHub

Verilog-A and overall accuracy settings

Fast-spice, or multi-rate simulators use different time-steps for each partition in a circuit. This can have some interesting consequences when used along with Verilog-A models.

Verilog-A allows one to use model some aspects of the circuit behavior using events. Unfortunately, sometimes the containing partition of the Verilog-A model uses very aggressive optimization for reducing the number of time-steps evaluated. This can cause the simulator to never evaluate the Verilog-A event.

In this case one has to set fairly conservative settings for the containing block. So all the speed gain from the Verilog-A model is lost in the conservative settings for the next block. Watch out for the gotcha!

A better Bash command-prompt

Bash sets the command prompt using the variable PS1, which is evaluated every time the prompt is printed. The following script, stolen from various parts of the internet (sorry, I didn’t keep references), is quite handy. It lists the username, host, working directory, and git/svn branch.

if [[ $- == *i* ]] ; then
    function parse_git_branch () {
        git name-rev HEAD 2>/dev/null | sed 's#HEAD\ \(.*\)#git:\1#'
    function parse_svn_branch() {
        local rurl=`svn info --show-item relative-url 2>/dev/null`
        [[ -n "$rurl" ]] && echo "svn:${rurl}@`svn info --show-item revision`"

    function proml() {
        local BOLD="\[$(tput bold)\]"
        local RED="\[$(tput setaf 1)\]"
        local GREEN="\[$(tput setaf 2)\]"
        local YELLOW="\[$(tput setaf 3)\]"
        local BLUE="\[$(tput setaf 4)\]"
        local PURPLE="\[$(tput setaf 5)\]"
        local CYAN="\[$(tput setaf 6)\]"
        local WHITE="\[$(tput setaf 7)\]"
        local GRAY="\[$(tput setaf 8)\]"
        local DEFAULT="\[$(tput sgr0)\]"

        PS1="${PURPLE}\u${GREEN}@\h ${YELLOW}\w${CYAN} "
        PS1+="\`parse_git_branch\`\`parse_svn_branch\`${DEFAULT}\n> "

Free SPICE simulators

Free SPICE simulators are great for practicing circuit design. There are a few free ones that are actively developed:
1. LTspice
2. ngspice
3. Xyce

LTspice comes bundled with a full suite for circuit design: schematic editor, simulator, and waveform viewer. It is actively developed, reasonably fast and very easy to use.

ngspice has a lot more features, including some scripting, and optional integration with TCL (tclspice), or even GPU processing (CUSPICE). ngspice is also Free (as in freedom) software licensed under the modified BSD license.

Xyce is developed at Sandia National Laboratories. It’s main feature is support for parallel multi-core simulation. Xyce is also Free software licensed under GPL v3.