# read table data
from pygmid import Lookup as lk
import numpy as np
lv_nmos = lk('sg13_lv_nmos.mat')
# list of parameters: VGS, VDS, VSB, L, W, NFING, ID, VT, GM, GMB, GDS, CGG, CGB, CGD, CGS, CDD, CSS, STH, SFL
# if not specified, minimum L, VDS=max(vgs)/2=0.9 and VSB=0 are used Sizing for Different Current Mirror Topologies
Copyright 2025 Harald Pretl
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
We want to compare the performance of different current mirror topologies. For this, we will size them for a given output current and minimum compliance voltage of 0.4V.
# define the common values
id_spec = 50e-6
vsat_spec = 0.4# current mirror 1: standard (basic) current mirror
# We use maximum length, and would select gm/Id of 3 to 5, because we are only bound by Vds,sat, and lower is better for current sources
gm_id_spec1 = 5
l_spec1 = 5
vgs1 = lv_nmos.look_upVGS(GM_ID=gm_id_spec1, L=l_spec1, VDS=vsat_spec, VSB=0.0)
print('Vgs =', round(vgs1/1e-3, 1), 'mV')
gm1 = gm_id_spec1 * id_spec
gm_gds1 = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec1, L=l_spec1, VDS=vsat_spec, VSB=0)
gds1 = gm1 / gm_gds1
print('gds =', round(gds1/1e-6, 3), 'uS, rout =', rout1:=round(1/gds1/1e3,2), 'kOhm')
id_w1 = lv_nmos.lookup('ID_W', GM_ID=gm_id_spec1, L=l_spec1, VDS=vsat_spec, VSB=0)
w1 = id_spec / id_w1
print('W =', round(w1, 2), 'um, rounded W =', round(w1*2)/2, 'um')Vgs = 593.4 mV
gds = 9.397 uS, rout = 106.42 kOhm
W = 9.42 um, rounded W = 9.5 um
# current mirror 2: cascoded current mirror
gm_id_spec2 = 10
l_spec2 = 2
vgs2 = lv_nmos.look_upVGS(GM_ID=gm_id_spec2, L=l_spec2, VDS=vsat_spec/2, VSB=0.0)
print('Vgs =', round(vgs2/1e-3, 1), 'mV')
gm2 = gm_id_spec2 * id_spec
gm_gds2 = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec2, L=l_spec2, VDS=vsat_spec/2, VSB=0)
gds2 = gm2 / gm_gds2
print('gds =', round(gds2/1e-6, 3), 'uS')
id_w2 = lv_nmos.lookup('ID_W', GM_ID=gm_id_spec2, L=l_spec2, VDS=vsat_spec/2, VSB=0)
w2 = id_spec / id_w2
print('W =', round(w2, 2), 'um, rounded W =', round(w2*2)/2, 'um')
gds_total2 = gds2 * gds2 / (gds2 + gm2 + gds2)
print('Total gds =', round(gds_total2/1e-6, 3), 'uS, rout =', rout2:=round(1/gds_total2/1e3,2), 'kOhm')Vgs = 405.2 mV
gds = 35.257 uS
W = 13.22 um, rounded W = 13.0 um
Total gds = 2.179 uS, rout = 458.96 kOhm
# current mirror 3: resistively degenerated current mirror
gm_id_spec3 = 10
l_spec3 = 3
vgs3 = lv_nmos.look_upVGS(GM_ID=gm_id_spec3, L=l_spec3, VDS=vsat_spec/2, VSB=0.0)
print('Vgs =', round(vgs3/1e-3, 1), 'mV')
gm3 = gm_id_spec3 * id_spec
gm_gds3 = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec3, L=l_spec3, VDS=vsat_spec/2, VSB=0)
gds3 = gm3 / gm_gds3
print('gds =', round(gds3/1e-6, 3), 'uS')
id_w3 = lv_nmos.lookup('ID_W', GM_ID=gm_id_spec3, L=l_spec3, VDS=vsat_spec/2, VSB=0)
w3 = id_spec / id_w3
print('W =', round(w3, 2), 'um, rounded W =', round(w3*2)/2, 'um')
r_deg = (vsat_spec/2) / id_spec
print('Rdeg =', round(r_deg/1e3,2), 'kOhm')
gds_total3 = gds3 * (1/r_deg) / (gds2 + gm2 + (1/r_deg))
print('Total gds =', round(gds_total3/1e-6, 3), 'uS, rout =', rout2:=round(1/gds_total3/1e3,2), 'kOhm')Vgs = 395.5 mV
gds = 33.332 uS
W = 18.4 um, rounded W = 18.5 um
Rdeg = 4.0 kOhm
Total gds = 10.612 uS, rout = 94.24 kOhm
# current mirror 4: regulated current mirror
gm_id_spec4 = 10
l_spec4 = 1
vgs4 = lv_nmos.look_upVGS(GM_ID=gm_id_spec4, L=l_spec4, VDS=vsat_spec*3/4, VSB=0.0)
print('Vgs (bottom) =', round(vgs4/1e-3, 1), 'mV')
gm4 = gm_id_spec4 * id_spec
gm_gds4 = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec4, L=l_spec4, VDS=vsat_spec*3/4, VSB=0)
gds4 = gm4 / gm_gds4
print('gds (bottom) =', round(gds4/1e-6, 3), 'uS')
id_w4 = lv_nmos.lookup('ID_W', GM_ID=gm_id_spec4, L=l_spec4, VDS=vsat_spec*3/4, VSB=0)
w4 = id_spec / id_w4
print('W =', round(w4, 2), 'um, rounded W =', round(w4*2)/2, 'um')
gm_gds4_top = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec4, L=l_spec4, VDS=vsat_spec*1/4, VSB=0)
gds4_top = gm4 / gm_gds4_top
print('gds (top) =', round(gds4_top/1e-6, 3), 'uS')
# additional stage
gm_id_spec4_aux = 25
id_aux = 5e-6
l_spec4_aux = 0.13
vgs4_aux = lv_nmos.look_upVGS(GM_ID=gm_id_spec4_aux, L=l_spec4_aux, VDS=0.5, VSB=0.0)
print('Vgs (amp) =', round(vgs4_aux/1e-3, 1), 'mV')
gm4_aux = gm_id_spec4_aux * id_aux
print('gm (amp) =', round(gm4_aux/1e-6, 3), 'uS')
gm_gds4_aux = lv_nmos.lookup('GM_GDS', GM_ID=gm_id_spec4_aux, L=l_spec4_aux, VDS=0.5, VSB=0)
gds4_aux = gm4_aux / gm_gds4_aux
print('gds (amp) =', round(gds4_aux/1e-6, 3), 'uS')
id_w4_aux = lv_nmos.lookup('ID_W', GM_ID=gm_id_spec4_aux, L=l_spec4_aux, VDS=0.5, VSB=0)
w4_aux = id_aux / id_w4_aux
print('W (amp) =', round(w4_aux, 2), 'um, rounded W =', round(w4_aux*2)/2, 'um')
# calculate total output conductance
rout4 = 1/gds4 + 1/gds4_top + gm4*(1+gm4_aux/gds4_aux)/(gds4_top*gds4)
gds_total4 = 1/rout4
print('Total gds =', round(gds_total4/1e-6, 3), 'uS, rout =', round(rout4/1e3,2), 'kOhm')Vgs (bottom) = 414.4 mV
gds (bottom) = 22.418 uS
W = 9.45 um, rounded W = 9.5 um
gds (top) = 237.572 uS
Vgs (amp) = 368.6 mV
gm (amp) = 125.0 uS
gds (amp) = 7.349 uS
W (amp) = 8.5 um, rounded W = 8.5 um
Total gds = 0.575 uS, rout = 1739.43 kOhm