Merge pull request #50 from umarcor/umarcor/ci-klayout
ci/Tests: bump Klayout to 0.27.10
diff --git a/models/ngspice/testing/.spiceinit b/models/ngspice/testing/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx
new file mode 100644
index 0000000..4910b80
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx
new file mode 100644
index 0000000..045b1a3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx
new file mode 100644
index 0000000..054b06a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx
new file mode 100644
index 0000000..d9db37a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx
new file mode 100644
index 0000000..d8d27d0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx
new file mode 100644
index 0000000..2492ba0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx
new file mode 100644
index 0000000..6ac6163
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx
new file mode 100644
index 0000000..2c7d454
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx
new file mode 100644
index 0000000..1b769d4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx
new file mode 100644
index 0000000..f5a1c99
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx
new file mode 100644
index 0000000..6ac941a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx
new file mode 100644
index 0000000..4989747
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx
new file mode 100644
index 0000000..26898b2
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx
new file mode 100644
index 0000000..96b07d9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx
new file mode 100644
index 0000000..76d04f4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx
new file mode 100644
index 0000000..a80ee45
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx
new file mode 100644
index 0000000..335d907
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx
new file mode 100644
index 0000000..364ca47
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx
new file mode 100644
index 0000000..ac6108e
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx
new file mode 100644
index 0000000..af592f1
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx
new file mode 100644
index 0000000..443c2de
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx
new file mode 100644
index 0000000..8fa1a77
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx
new file mode 100644
index 0000000..fec8e82
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx
new file mode 100644
index 0000000..1b61641
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx
new file mode 100644
index 0000000..0ef13b4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx
new file mode 100644
index 0000000..4711b88
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx
new file mode 100644
index 0000000..3585f1a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx
new file mode 100644
index 0000000..5582df3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx
new file mode 100644
index 0000000..d9d62db
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx
new file mode 100644
index 0000000..ccd2908
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx
new file mode 100644
index 0000000..25d7edf
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..cbf4db3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..f4527e0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..7023e41
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..08807f6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..1b2af92
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..0c964d8
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..5789d79
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9321fb6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..bed9727
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..c45be6c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..2fc2a7c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9cbce9b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6767fc
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx
new file mode 100644
index 0000000..8b9344f
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx
new file mode 100644
index 0000000..efd1039
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx
new file mode 100644
index 0000000..c763f6b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx
new file mode 100644
index 0000000..0d975fe
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx
new file mode 100644
index 0000000..674174c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx
new file mode 100644
index 0000000..bbadf6c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx
new file mode 100644
index 0000000..dfd13b6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx
new file mode 100644
index 0000000..8e5eb30
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx
new file mode 100644
index 0000000..ca38856
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx
new file mode 100644
index 0000000..b2f245d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx
new file mode 100644
index 0000000..d337742
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx
new file mode 100644
index 0000000..c6d8642
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx
new file mode 100644
index 0000000..bc8639c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..813635c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..fd139e1
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6d80ee
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx
new file mode 100644
index 0000000..407129c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..1dbd85c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..b6b74f9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx
new file mode 100644
index 0000000..dd48b9d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..cba86be
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..5ceb2a6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..b4b36f9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9842b38
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx
new file mode 100644
index 0000000..3a70d17
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx
new file mode 100644
index 0000000..ab51a89
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx
new file mode 100644
index 0000000..3662177
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..3aee704
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..bb8e4f4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..865ff15
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..2fa2797
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx
new file mode 100644
index 0000000..f7ba8ac
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..d87ad5d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..c7cf1cc
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..16c3a3a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..a9254cb
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx
new file mode 100644
index 0000000..aebf3e1
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..a824873
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..10eaca3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..8cb3a2d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..1416ab9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..89a74f0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..e7085f0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..ad30f91
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..19118d4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..48791d7
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..a85c6c3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..be21ff6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx
new file mode 100644
index 0000000..3ef61df
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx
new file mode 100644
index 0000000..0f18273
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..a3030a1
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx
new file mode 100644
index 0000000..f191aaa
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..3262949
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx
new file mode 100644
index 0000000..a4830f9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..51142f0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..4cafde6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..274347d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..a139dce
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..86f4631
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..c4bd383
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..b9314cc
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..c081636
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..bb5c8e7
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..84cfe2f
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..57c0b79
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..623e9ba
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..40744c4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..cb09382
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..0dbd278
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..20315b0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..2df4e8f
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..5640c40
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..4ba2549
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..10b193a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..260a8ca
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..e65b1fc
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..ff7ad36
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..76fea8b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..1a49e0c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..0fbb672
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..6e0a4b1
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..b043825
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx
new file mode 100644
index 0000000..6408995
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx
new file mode 100644
index 0000000..7cbd776
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx
new file mode 100644
index 0000000..12f2d07
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..0d06e5e
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..2687b8c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..c624220
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..e06c435
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..d48576b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..fa0909e
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..479a205
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..6d890fa
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..fba4fbc
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..31fcb16
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..a4188bd
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..a26d9ba
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..64025b9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..d9c246d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..5bb2231
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..77df6d2
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..da255df
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..6896518
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..04bb357
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..d7d005b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..02146f8
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..2d1275d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..7c03c8a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..de70746
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..758a7f4
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx
new file mode 100644
index 0000000..76c959a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx
new file mode 100644
index 0000000..5ad8dfe
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..71aa86d
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..1e91eb0
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..8347b1c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx
new file mode 100644
index 0000000..4b15b25
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx
new file mode 100644
index 0000000..430db7b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..bbcecca
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx
new file mode 100644
index 0000000..6632263
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..0c2cd97
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx
new file mode 100644
index 0000000..26afc55
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx
new file mode 100644
index 0000000..d587bc9
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..75bb7db
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx
new file mode 100644
index 0000000..f258c60
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..f303906
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx
new file mode 100644
index 0000000..7fb2330
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx
new file mode 100644
index 0000000..731c3ce
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..e11f941
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..40b1ff2
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..332dbc3
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx
new file mode 100644
index 0000000..6386669
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx
new file mode 100644
index 0000000..359b06c
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx
new file mode 100644
index 0000000..597ed2a
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx
new file mode 100644
index 0000000..7af7d6b
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx
new file mode 100644
index 0000000..8e224e2
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx
new file mode 100644
index 0000000..7400b88
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx
new file mode 100644
index 0000000..7d5f551
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx
new file mode 100644
index 0000000..18592fa
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx
new file mode 100644
index 0000000..214f560
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx
new file mode 100644
index 0000000..acbbf89
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx
new file mode 100644
index 0000000..c7701c8
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx
new file mode 100644
index 0000000..88ec343
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx
new file mode 100644
index 0000000..b414848
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx
new file mode 100644
index 0000000..5dc8f38
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx
new file mode 100644
index 0000000..8639044
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx
new file mode 100644
index 0000000..aab88bd
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx
new file mode 100644
index 0000000..596dfb6
--- /dev/null
+++ b/models/ngspice/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/Makefile b/models/ngspice/testing/Makefile
new file mode 100644
index 0000000..e22efb4
--- /dev/null
+++ b/models/ngspice/testing/Makefile
@@ -0,0 +1,182 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=================================================================
+# ------------------------ models_ngspice ------------------------
+#=================================================================
+
+SHELL := /bin/bash
+Testing_DIR ?= $(shell pwd)
+run_folder := $(shell date +'run_%Y_%m_%d_%H:%M:%S')
+
+
+.DEFAULT_GOAL := all
+
+all : test-models_ngspice
+
+test-models_ngspice: smoke-test models-MOS models-BJT models-diode models-MOSCAP models-MIMCAP models-RES
+
+#================================
+#---------- Create Run ----------
+#================================
+
+.ONESHELL:
+Add_run-dir:
+ @cd $(Testing_DIR)
+ @ [ ! -d "$(run_folder)/" ] && cp -rf $(Testing_DIR)/regression $(Testing_DIR)/$(run_folder)
+
+#================================
+#---------- smoke-test ----------
+#================================
+
+.ONESHELL:
+smoke-test:
+ @cd $(Testing_DIR)/smoke_test
+ @python3 ng_smoke_test.py
+
+
+#================================
+# ---------- models-MOS----------
+#================================
+
+.ONESHELL:
+models-MOS: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/mos_iv_vgs
+ @echo "========== Runing models_ngspice-MOS regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+# @cd ../mos_iv_vbs
+# @python3 models_regression.py |& tee -a ../run_log.log
+
+# .ONESHELL:
+# clean-models-MOS:
+# @echo "==== Cleaning models-MOS old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/mos_iv_vgs && rm -rf nmos_* pmos_*
+
+#================================
+# ---------- models-BJT----------
+#================================
+
+.ONESHELL:
+models-BJT: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/bjt_cj
+ @echo "========== Runing models_ngspice-BJT regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-BJT:
+# @echo "==== Cleaning models-BJT old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/bjt_cj && rm -rf vnpn_* vpnp_*
+
+#================================
+# --------- models-diode --------
+#================================
+
+.ONESHELL:
+models-diode: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/diode
+ @echo "========== Runing models_ngspice-didoe regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-diode:
+# @echo "==== Cleaning models-diode old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/diode && rm -rf np_* pn_* nwp* dnw* sc_*
+
+#================================
+# -------- models-MOSCAP --------
+#================================
+
+.ONESHELL:
+models-MOSCAP: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/moscap_c
+ @echo "========== Runing models_ngspice-MOSCAP regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-MOSCAP:
+# @echo "==== Cleaning models-MOSCAP old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/moscap_c && rm -rf nmoscap_* pmoscap_*
+
+#================================
+# -------- models-MIMCAP --------
+#================================
+
+.ONESHELL:
+models-MIMCAP: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/mimcap_c
+ @echo "========== Runing models_ngspice-MIMCAP regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-MIMCAP:
+# @echo "==== Cleaning models-MIMCAP old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/mimcap_c && rm -rf mim*
+
+
+#================================
+# --------- models-RES ----------
+#================================
+
+.ONESHELL:
+models-RES: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/resistor_r
+ @echo "========== Runing models_ngspice-RES regression ==========" |& tee -a ../run_log.log
+ @python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-RES:
+# @echo "==== Cleaning models-RES old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/resistor_r && rm -rf nplus* pplus* npoly* ppoly* rm* tm* nwell
+
+
+
+# #===============================
+# # --------- Clean ALL ----------
+# #===============================
+
+# .ONESHELL:
+# clean: clean-models-MOS clean-models-BJT clean-models-diode clean-models-MOSCAP clean-models-MIMCAP clean-models-RES
+# @echo "==== Cleaning all runs is done ===="
+
+
+#==========================
+# --------- HELP ----------
+#==========================
+
+# Help Target
+help:
+ @echo "\n ==== The following are some of the valid targets for this Makefile ====\n"
+ @echo "... all (the default if no target is provided)"
+ @echo "... smoke-test (To run smoke test for an inverter)"
+ @echo "... test-models_ngspice (To run regression for all devices)"
+ @echo "... models-MOS (To run regression for MOS devices)"
+ @echo "... models-BJT (To run regression for BJT devices)"
+ @echo "... models-diode (To run regression for diode devices)"
+ @echo "... models-MOSCAP (To run regression for MOSCAP devices)"
+ @echo "... models-MIMCAP (To run regression for MIMCAP devices)"
+ @echo "... models-RES (To run regression for RES devices)"
+# @echo "... clean-models-MOS (To clean old runs for MOS devices)"
+# @echo "... clean-models-BJT (To clean old runs for BJT devices)"
+# @echo "... clean-models-diode (To clean old runs for diode devices)"
+# @echo "... clean-models-MOSCAP (To clean old runs for MOSCAP devices)"
+# @echo "... clean-models-MIMCAP (To clean old runs for MIMCAP devices)"
+# @echo "... clean-models-RES (To run regression for RES devices)"
+# @echo "... clean (To clean all old runs) "
+
+.PHONY : help
diff --git a/models/ngspice/testing/README.md b/models/ngspice/testing/README.md
new file mode 100644
index 0000000..2205747
--- /dev/null
+++ b/models/ngspice/testing/README.md
@@ -0,0 +1,51 @@
+# Globalfoundries 180nm MCU models-ngspice regression
+
+Explains how to run GF180nm models-ngspice regression.
+
+## Folder Structure
+
+```text
+📦testing
+ ┣ 📜Makefile
+ ┣ 📜README.md
+ ┣ 📦regression
+ ┣ 📦smoke_test
+ ┣ 📦180MCU_SPICE_Models
+ ```
+
+## Prerequisites
+
+At a minimum:
+
+- Git 2.35+
+- Python 3.6+
+- ngspice-36+
+
+### On Ubuntu, you can just
+
+`apt install -y build-essential python3`
+
+- Check this [ngspice](http://ngspice.sourceforge.net/download.html) for ngspice installation.
+
+## Regression Usage
+
+To make a full test for GF180nm models-ngspice, you could use the following command in testing directory:
+
+```bash
+make all
+```
+
+- You could also check allowed targets in the Makefile, using the following command:
+
+ ```bash
+ make help
+ ```
+
+## **Regression Outputs**
+
+- The resulting files are in `regression/<device_folder>/` with name of `<device_name><options>` that contains:
+
+ 1. A final report file of all results.
+ 2. measured folder that contains measured data used in regression.
+ 3. simulated folder that contains simulated data used in regression.
+ 4. netlists folder that contains spice files used in simulation.
diff --git a/models/ngspice/testing/regression/.spiceinit b/models/ngspice/testing/regression/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/regression/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/bjt_beta/.spiceinit b/models/ngspice/testing/regression/bjt_beta/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_beta/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/bjt_beta/device_netlists/npn.spice b/models/ngspice/testing/regression/bjt_beta/device_netlists/npn.spice
new file mode 100644
index 0000000..e25a8d2
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_beta/device_netlists/npn.spice
@@ -0,0 +1,29 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 3
+Vbp b 0 dc 1.2
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xq1 c b 0 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vbp 0.2 1.2 0.01 Vcp 1 3 1
+wrdata npn/simulated_Ic/{{i}}_simulated_{{device}}.csv -i(Vcp)
+wrdata npn/simulated_Ib/{{i}}_simulated_{{device}}.csv -i(Vbp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_beta/device_netlists/pnp.spice b/models/ngspice/testing/regression/bjt_beta/device_netlists/pnp.spice
new file mode 100644
index 0000000..d6a9414
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_beta/device_netlists/pnp.spice
@@ -0,0 +1,29 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc -3
+Vbp b 0 dc -1.2
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xq1 c b 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vbp -0.2 -1.2 -0.01 Vcp -1 -3 -1
+wrdata pnp/simulated_Ic/{{i}}_simulated_{{device}}.csv i(Vcp)
+wrdata pnp/simulated_Ib/{{i}}_simulated_{{device}}.csv i(Vbp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice b/models/ngspice/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice
new file mode 100644
index 0000000..b21c115
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice
@@ -0,0 +1,34 @@
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 3
+*Ib 0 b 9u
+Vbp b 0 dc 1.2
+
+.temp 25
+.options tnom=25
+
+xq1 c b 0 0 vnpn_10x10
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vbp 0.2 1.2 0.01 Vcp 1 3 1
+print -i(Vcp)
+print -i(Vbp)
+wrdata result.csv -i(Vcp)
+wrdata result.csv -i(Vbp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
+
diff --git a/models/ngspice/testing/regression/bjt_beta/models_regression.py b/models/ngspice/testing/regression/bjt_beta/models_regression.py
new file mode 100644
index 0000000..6d8c5f2
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_beta/models_regression.py
@@ -0,0 +1,243 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from cmath import inf
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vb,step,Id_sim,list_devices,vc):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+
+ # Extracting measured values for each Device
+ for i in range (loops):
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ # Special case for 1st measured values
+ if i == 0 :
+ if device == "pnp":
+ temp_vb = vb
+ vb = "-vb "
+ # measured Id_sim 0
+ col_list = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[0]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+ if device == "pnp":
+ vb = temp_vb
+
+ # measured Id_sim 1
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i+1}",f"{vc}{step[1]}.{2*i+1}",f"{vc}{step[2]}.{2*i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[1]}/{i}_measured_{list_devices[k]}.csv", index = False)
+ else:
+ # measured Id_sim 0
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i}",f"{vc}{step[1]}.{2*i}",f"{vc}{step[2]}.{2*i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[0]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+ # measured Id_sim 1
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i+1}",f"{vc}{step[1]}.{2*i+1}",f"{vc}{step[2]}.{2*i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[1]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+def ext_simulated(device,vc,step,sweep,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ netlist_tmp = f"./device_netlists/{device}.spice"
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_sim",exist_ok=True)
+ with open(f"{device}/{device}_netlists_sim/{i}_{device}_netlist_{list_devices[k]}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = list_devices[k], i = i , temp = temp ))
+ netlist_path = f"{device}/{device}_netlists_sim/{i}_{device}_netlist_{list_devices[k]}.spice"
+
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data 0
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",header=None, delimiter=r"\s+")
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 1]
+
+ # Writing final simulated data 0
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+
+ # Writing simulated data 1
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",header=None, delimiter=r"\s+")
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 1]
+
+ # Writing final simulated data 1
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+def error_cal(device,vb,step,Id_sim,list_devices,vc):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ measured = pd.read_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[0:, 1]) - abs(simulated.iloc[0:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[0:, 2]) - abs(simulated.iloc[0:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[0:, 3]) - abs(simulated.iloc[0:, 3]))/abs(measured.iloc[:, 3])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3]).transpose()
+ df_error.replace([np.inf, -np.inf], df_error.max().nlargest(2).iloc[1], inplace=True)
+ df_error.to_csv(f"{device}/error_{Id_sim}/{i}_{device}_error_{list_devices[k]}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{vc}{step[0]}"].mean() + df_error[f"{vc}{step[1]}"].mean() + df_error[f"{vc}{step[2]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vc = (df_error == max_error).idxmax(axis=1)[max_index]
+ if Id_sim == "Ic":
+ if i == 0 :
+ if device == "pnp":
+ temp_vb = vb
+ vb = "-vb "
+ else:
+ if device == "pnp":
+ vb = temp_vb
+ max_location_vb = df_error[f"{vb}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'device': f'{list_devices[k]}','Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vc} & Vc (V) = {max_location_vb}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["npn","pnp"]
+ list_devices = [["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4", "vnpn_0p54x2"],
+ ["vpnp_0p42x10" , "vpnp_0p42x5", "vpnp_10x10" , "vpnp_5x5"]]
+ vb = ["vbp ","-vb (V)"]
+ vc = ["vcp =","vc =-"]
+ Id_sim = ["Ic","Ib"]
+ sweep = 101
+ step = [1, 2, 3]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{Id_sim[1]}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/bjt_{device}_beta_f.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{Id_sim[1]}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim[1]}",exist_ok=False)
+
+ # =========== Simulate ==============
+ ext_measured (device,vb[i],step,Id_sim,list_devices[i],vc[i])
+
+ ext_simulated(device,vb[i],step,sweep,Id_sim,list_devices[i],vc[i])
+
+ # ============ Results =============
+ error_cal (device,vb[i],step,Id_sim[0],list_devices[i],vc[i])
+ error_cal (device,vb[i],step,Id_sim[1],list_devices[i],vc[i])
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/bjt_cj/.spiceinit b/models/ngspice/testing/regression/bjt_cj/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/.spiceinit b/models/ngspice/testing/regression/bjt_cj/device_netlists/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/damping_test.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/damping_test.spice
new file mode 100644
index 0000000..cf16aa3
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/damping_test.spice
@@ -0,0 +1,43 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+Vb b 0 dc -3.3
+
+.temp 25
+.options tnom=25
+
+
+xq1 0 b 0 0 vnpn_10x10
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[cpi]
+run
+print @q.xq1.q0[cpi]
+
+.endc
+
+** library calling
+
+.include "../../../../design.ngspice"
+.lib "../../../../sm141064.ngspice" bjt_typical
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CBJ.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CBJ.spice
new file mode 100644
index 0000000..5b9e251
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CBJ.spice
@@ -0,0 +1,44 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vb b 0 dc -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xq1 0 b 0 0 {{device}}
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[cmu]
+run
+print @q.xq1.q0[cmu]
+
+wrdata {{device}}_{{cv_sim}}_{{corner}}_T{{temp}}/simulated_{{cv_sim}}/1_simulated_{{device}}.csv @q.xq1.q0[cmu]*1.0e15
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_{{corner}}
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CSJ.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CSJ.spice
new file mode 100644
index 0000000..0a09963
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_CSJ.spice
@@ -0,0 +1,44 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vb b 0 dc -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xq1 0 0 0 b {{device}}
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[csub]
+run
+print @q.xq1.q0[csub]
+
+wrdata {{device}}_{{cv_sim}}_{{corner}}_T{{temp}}/simulated_{{cv_sim}}/2_simulated_{{device}}.csv @q.xq1.q0[csub]*1.0e15
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_{{corner}}
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_EBJ.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_EBJ.spice
new file mode 100644
index 0000000..e735dbc
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/npn_EBJ.spice
@@ -0,0 +1,44 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vb b 0 dc -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xq1 0 b 0 0 {{device}}
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[cpi]
+run
+print @q.xq1.q0[cpi]
+
+wrdata {{device}}_{{cv_sim}}_{{corner}}_T{{temp}}/simulated_{{cv_sim}}/0_simulated_{{device}}.csv @q.xq1.q0[cpi]*1.0e15
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_{{corner}}
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_CBJ.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_CBJ.spice
new file mode 100644
index 0000000..9d1a0ee
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_CBJ.spice
@@ -0,0 +1,44 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vb b 0 dc -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xq1 b 0 0 {{device}}
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[cmu]
+run
+print @q.xq1.q0[cmu]
+
+wrdata {{device}}_{{cv_sim}}_{{corner}}_T{{temp}}/simulated_{{cv_sim}}/1_simulated_{{device}}.csv @q.xq1.q0[cmu]*1.0e15
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_{{corner}}
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_EBJ.spice b/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_EBJ.spice
new file mode 100644
index 0000000..521b223
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/device_netlists/pnp_EBJ.spice
@@ -0,0 +1,44 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vb b 0 dc -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xq1 0 0 b {{device}}
+
+
+.DC Vb 0 -3.3 -0.1
+
+.control
+
+save all @q.xq1.q0[cpi]
+run
+print @q.xq1.q0[cpi]
+
+wrdata {{device}}_{{cv_sim}}_{{corner}}_T{{temp}}/simulated_{{cv_sim}}/0_simulated_{{device}}.csv @q.xq1.q0[cpi]*1.0e15
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_{{corner}}
+
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_cj/models_regression.py b/models/ngspice/testing/regression/bjt_cj/models_regression.py
new file mode 100644
index 0000000..aeba47a
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_cj/models_regression.py
@@ -0,0 +1,202 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start,dirpath):
+
+ # Get dimensions used for each device
+ if "vnpn" in device:
+ loops = 3
+ else:
+ loops = 2
+
+ # Extracting measured values for each W & L
+ for i in range (start,loops+start):
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_{device}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_{device}.csv", index = False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,temp,dirpath,cap):
+
+ # Get dimensions used for each device
+ netlist_tmp = f"./device_netlists/{cap}.spice"
+
+ if "EBJ" in cap: i = 0
+ elif "CBJ" in cap: i = 1
+ else: i =2
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i}_{device}_netlist_{device}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, i = i , temp = temp, cv_sim = cv_sim , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i}_{device}_netlist_{device}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",index= False)
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",index= False)
+
+def error_cal(device,vn,d_in,cv_sim, corner,temp,dirpath):
+
+ # Get dimensions used for each device
+ loops = 2
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ if i == 0 : cap = "EBJ"
+ elif i == 1 : cap = "CBJ"
+ else : cap = "CSJ"
+
+ measured = pd.read_csv(f"{dirpath}/measured_{cv_sim}/{i}_measured_{device}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{cv_sim}/{i}_{device}_error_{device}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}_{corner}"].mean())
+ # Max error
+ max_error = df_error[f"{d_in}_{corner}"].max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vn}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'corner': f'{corner}' , 'Junction': f'{cap}', 'Simulated_Val':f'{cv_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vn (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{cv_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{cv_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ corners = ["typical","ff","ss"]
+ temps = [ 25 , -40 , 175 ]
+ measure = ["cv","Vj", "bjt", 31]
+ cv_sim, bjt_vn, bjt_in = measure[0], measure[1], measure[2]
+
+ #vnpn
+ npn_devices = ["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4" , "vnpn_0p54x2"]
+ npn_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in npn_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/{bjt_in}_{cv_sim}_npn.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+ ext_measured (device,bjt_vn,bjt_in, cv_sim, corner,npn_start,dirpath)
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_EBJ")
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_CBJ")
+ # ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_CSJ")
+ error_cal (device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath)
+
+ npn_start = npn_start + 3
+ npn_start = 0
+
+ # vpnp
+ pnp_devices = ["vpnp_0p42x10" , "vpnp_0p42x5" , "vpnp_10x10" , "vpnp_5x5"]
+ pnp_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in pnp_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/{bjt_in}_{cv_sim}_pnp.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+ ext_measured (device,bjt_vn,bjt_in, cv_sim, corner,pnp_start,dirpath)
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"pnp_EBJ")
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"pnp_CBJ")
+ error_cal (device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath)
+
+ pnp_start = pnp_start + 2
+ pnp_start = 0
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/bjt_iv/.spiceinit b/models/ngspice/testing/regression/bjt_iv/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_iv/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/bjt_iv/device_netlists/npn.spice b/models/ngspice/testing/regression/bjt_iv/device_netlists/npn.spice
new file mode 100644
index 0000000..792e40c
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_iv/device_netlists/npn.spice
@@ -0,0 +1,29 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 6
+Ib 0 b 9u
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xq1 c b 0 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vcp 0 6 0.1 Ib 1u 9u 2u
+print -i(Vcp)
+wrdata npn/simulated_IcVc/{{i}}_simulated_{{device}}.csv -i(Vcp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_iv/device_netlists/pnp.spice b/models/ngspice/testing/regression/bjt_iv/device_netlists/pnp.spice
new file mode 100644
index 0000000..0233ac6
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_iv/device_netlists/pnp.spice
@@ -0,0 +1,29 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc -3
+Ib 0 b -9u
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xq1 c b 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vcp 0 -3 -0.1 Ib -1u -9u -2u
+print -i(Vcp)
+wrdata pnp/simulated_IcVc/{{i}}_simulated_{{device}}.csv i(Vcp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice b/models/ngspice/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice
new file mode 100644
index 0000000..48f9bb3
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice
@@ -0,0 +1,30 @@
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 6
+Ib 0 b 9u
+
+.temp 25
+.options tnom=25
+
+xq1 c b 0 0 vnpn_10x10
+
+
+*****************
+** Analysis
+*****************
+
+.control
+set filetype=ascii
+
+dc Vcp 0 6 0.1 Ib 1u 9u 2u
+print -i(Vcp)
+wrdata result.csv -i(Vcp)
+.endc
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" bjt_typical
+
+.end
diff --git a/models/ngspice/testing/regression/bjt_iv/models_regression.py b/models/ngspice/testing/regression/bjt_iv/models_regression.py
new file mode 100644
index 0000000..a6630d0
--- /dev/null
+++ b/models/ngspice/testing/regression/bjt_iv/models_regression.py
@@ -0,0 +1,208 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vc,step,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+
+ # Extracting measured values for each Device
+ for i in range (loops):
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ # Special case for 1st measured values
+ if i == 0 :
+ if device == "pnp":
+ temp_vc = vc
+ vc = "-vc "
+ # measured Id_sim
+ col_list = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv", index = False)
+ else:
+ if device == "pnp":
+ vc = temp_vc
+ # measured Id_sim
+ col_list = [f"{vc}",f"{ib}{step[0]}.{i}",f"{ib}{step[1]}.{i}",f"{ib}{step[2]}.{i}",f"{ib}{step[3]}.{i}",f"{ib}{step[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+def ext_simulated(device,vc,step,sweep,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ netlist_tmp = f"./device_netlists/{device}.spice"
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{Id_sim}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_{list_devices[k]}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = list_devices[k], i = i , temp = temp ))
+ netlist_path = f"{device}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_{list_devices[k]}.spice"
+
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",header=None, delimiter=r"\s+")
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+def error_cal(device,vc,step,Id_sim,list_devices,ib):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ measured = pd.read_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[0:, 1]) - abs(simulated.iloc[0:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[0:, 2]) - abs(simulated.iloc[0:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[0:, 3]) - abs(simulated.iloc[0:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[0:, 4]) - abs(simulated.iloc[0:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[0:, 5]) - abs(simulated.iloc[0:, 5]))/abs(measured.iloc[:, 5])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ df_error.to_csv(f"{device}/error_{Id_sim}/{i}_{device}_error_{list_devices[k]}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{ib}{step[0]}"].mean() + df_error[f"{ib}{step[1]}"].mean() + df_error[f"{ib}{step[2]}"].mean() +
+ df_error[f"{ib}{step[3]}"].mean() + df_error[f"{ib}{step[4]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_ib = (df_error == max_error).idxmax(axis=1)[max_index]
+ if i == 0 :
+ if device == "pnp":
+ temp_vc = vc
+ vc = "-vc "
+ else:
+ if device == "pnp":
+ vc = temp_vc
+ max_location_vc = df_error[f"{vc}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'device': f'{list_devices[k]}','Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_ib} & Vc (V) = {max_location_vc}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["npn","pnp"]
+ list_devices = [["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4", "vnpn_0p54x2"],
+ ["vpnp_0p42x10" , "vpnp_0p42x5", "vpnp_10x10" , "vpnp_5x5"]]
+ vc = ["vcp ","-vc (A)"]
+ ib = ["ibp =","ib =-"]
+ Id_sim = "IcVc"
+ sweep = [61,31]
+ step = [ "1.000E-06" , "3.000E-06" , "5.000E-06" , "7.000E-06" , "9.000E-06"]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/bjt_{device}_icvc_f.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+
+ # =========== Simulate ==============
+ ext_measured (device,vc[i],step,Id_sim,list_devices[i],ib[i])
+
+ ext_simulated(device,vc[i],step,sweep[i],Id_sim,list_devices[i],ib[i])
+
+ # ============ Results =============
+ error_cal (device,vc[i],step,Id_sim,list_devices[i],ib[i])
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/diode/.spiceinit b/models/ngspice/testing/regression/diode/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/dnwps_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/dnwps_cv.nl_out.xlsx
new file mode 100644
index 0000000..cdcbf27
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/dnwps_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/dnwps_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/dnwps_iv.nl_out.xlsx
new file mode 100644
index 0000000..d9d62db
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/dnwps_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_cv.nl_out.xlsx
new file mode 100644
index 0000000..82a005e
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_iv.nl_out.xlsx
new file mode 100644
index 0000000..25d7edf
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/dnwpw_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..aea9c00
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..f4527e0
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/np_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..cd49396
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..08807f6
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/np_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..65e5dd5
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..0c964d8
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/nwp_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..725218c
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9321fb6
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/nwp_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..004bb41
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..c45be6c
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/pn_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..b9edd0a
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9cbce9b
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/pn_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx
new file mode 100644
index 0000000..05546ae
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx b/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx
new file mode 100644
index 0000000..1d60cf5
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/0_measured_data/sc_diode_iv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/diode/device_netlists/cv.spice b/models/ngspice/testing/regression/diode/device_netlists/cv.spice
new file mode 100644
index 0000000..c53922b
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/device_netlists/cv.spice
@@ -0,0 +1,55 @@
+***************************
+** CV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vn n 0 dc 1
+
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+dn n 0 {{device}} AREA = {{area}}p PJ = {{pj}}u
+
+.control
+set filetype=ascii
+
+let vn_min = 0
+let vn_step = -0.2
+let vn_max = -3.2
+
+option TEMP={{temp}}
+
+save @dn[cd]
+*******************
+** simulation part
+*******************
+DC Vn $&vn_min $&vn_max $&vn_step
+
+* ** parameters calculation
+
+print @dn[cd]*1000000000000000
+
+wrdata {{device}}_{{Id_sim}}_{{corner}}/simulated_{{Id_sim}}/{{i}}_simulated_A{{area}}_P{{pj}}.csv @dn[cd]*1000000000000000
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" diode_{{corner}}
+
+
+.end
\ No newline at end of file
diff --git a/models/ngspice/testing/regression/diode/device_netlists/iv.spice b/models/ngspice/testing/regression/diode/device_netlists/iv.spice
new file mode 100644
index 0000000..e66427c
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/device_netlists/iv.spice
@@ -0,0 +1,47 @@
+***************************
+** IV netlist generation **
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vn n 0 dc 1
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+
+dn1 n 0 {{device}} AREA = {{area}}p PJ = {{pj}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vn -12.13 1.13 0.13
+print abs(i(Vn))
+wrdata {{device}}_{{Id_sim}}_{{corner}}/simulated_{{Id_sim}}/{{i}}_simulated_A{{area}}_P{{pj}}.csv abs(i(Vn))
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" diode_{{corner}}
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/diode/models_regression.py b/models/ngspice/testing/regression/diode/models_regression.py
new file mode 100644
index 0000000..eddc91a
--- /dev/null
+++ b/models/ngspice/testing/regression/diode/models_regression.py
@@ -0,0 +1,196 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vn,d_in, Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[i]
+ length = dimensions["L (um)"].iloc[i]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", index = False)
+
+def ext_simulated(device,vn,d_in,vn_sweeps,Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ netlist_tmp = f"./device_netlists/{Id_sim}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+
+ if i % 4 == 0: temp = -40
+ elif i % 4 == 1: temp = 25
+ elif i % 4 == 2: temp = 125
+ else:
+ temp = 175
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{Id_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, area = width, pj = length, i = i , temp = temp, Id_sim = Id_sim , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+
+ # empty array to append in it shaped (vn_sweeps, number of trials + 1)
+ new_array = np.empty((vn_sweeps, 1+int(df_simulated.shape[0]/vn_sweeps)))
+ new_array[:, 0] = df_simulated.iloc[:vn_sweeps, 0]
+ times = int(df_simulated.shape[0]/vn_sweeps)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vn_sweeps:(j+1)*vn_sweeps, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i % 4 == 0: temp = -40
+ elif i % 4 == 1: temp = 25
+ elif i % 4 == 2: temp = 125
+ else:
+ temp = 175
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i}_{device}_error_A{width}_P{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}_{corner}"].mean())/6
+ # Max error
+ max_error = df_error[f"{d_in}_{corner}"].max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vn}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{dirpath}', 'Area': f'{width}', 'Perimeter': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vn (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["dnwps","dnwpw","np_3p3","np_6p0", "nwp_3p3","nwp_6p0","pn_3p3","pn_6p0","sc_diode"]
+ corners = ["typical","ff","ss"]
+ measures = [["iv","Vn1 (V)", " |In1(A)| diode", 103],
+ ["cv","Vj", "diode", 17]]
+
+ for device in devices:
+ for measure in measures:
+ for corner in corners:
+ # Folder structure of measured values
+ Id_sim, diode_vn, diode_in, no_of_vn_sweeps = measure[0], measure[1], measure[2], measure[3]
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{Id_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"./0_measured_data/{device}_{Id_sim}.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{Id_sim}",exist_ok=False)
+
+ ext_measured (device,diode_vn,diode_in, Id_sim, corner)
+
+ ext_simulated(device,diode_vn,diode_in,no_of_vn_sweeps,Id_sim, corner)
+ error_cal (device,diode_vn,diode_in,Id_sim, corner)
+
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/mimcap_c/.spiceinit b/models/ngspice/testing/regression/mimcap_c/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/mimcap_c/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/mimcap_c/device_netlists/.spiceinit b/models/ngspice/testing/regression/mimcap_c/device_netlists/.spiceinit
new file mode 100644
index 0000000..84222e2
--- /dev/null
+++ b/models/ngspice/testing/regression/mimcap_c/device_netlists/.spiceinit
@@ -0,0 +1,5 @@
+
+* user provided init file
+set ngbehavior=hs
+
+
diff --git a/models/ngspice/testing/regression/mimcap_c/device_netlists/mimcap.spice b/models/ngspice/testing/regression/mimcap_c/device_netlists/mimcap.spice
new file mode 100644
index 0000000..b2d8736
--- /dev/null
+++ b/models/ngspice/testing/regression/mimcap_c/device_netlists/mimcap.spice
@@ -0,0 +1,38 @@
+***************************
+** cap
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+.options noacct
+
+Ich 0 p dc 10u
+
+.temp 25
+.options tnom=25
+
+xcn p 0 {{device}} c_length={{length}}u c_width={{width}}u
+
+.ic v(p)=0.0
+.tran 10ns 100us
+
+* .print tran v(p) cj=par('10.0e-6 * time / v(p)')
+
+.meas tran CV FIND par('(10.0e-6 * time / v(p))*1.0e15') AT=100us
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" mimcap_{{corner}}
+.end
+
diff --git a/models/ngspice/testing/regression/mimcap_c/models_regression.py b/models/ngspice/testing/regression/mimcap_c/models_regression.py
new file mode 100644
index 0000000..ee4a895
--- /dev/null
+++ b/models/ngspice/testing/regression/mimcap_c/models_regression.py
@@ -0,0 +1,163 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ # measured cv
+ col_list = [f"{vn}",f"{d_in}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ netlist_tmp = f"./device_netlists/mimcap.spice"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice.log")
+ clean_data = str(df_simulated.loc[4]).replace("Compatibility modes selected: hs","").replace("\nName: 4, dtype: object","").split("=")
+ df_clean_ = {vn: [f"moscap_{corner}"],d_in: [clean_data[1]]}
+ df_clean = pd.DataFrame(df_clean_)
+ df_clean.to_csv(f"{dirpath}/simulated_{cv_sim}/{i-start}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ df_final = pd.DataFrame()
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i-start}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i-start}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i-start}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}"].mean())
+ # Max error
+ max_error = df_error[f"{d_in}"].max()
+
+ df_final_ = {'Run no.': f'{i-start}', 'Device name': f'{dirpath}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+
+def main():
+
+ # mim
+ corners = ["ss" , "typical","ff"]
+ devices = ["mim_1p5fF" , "mim_1p0fF" , "mim_2p0fF"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start)
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = start + 24
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/mos_cv/.spiceinit b/models/ngspice/testing/regression/mos_cv/.spiceinit
new file mode 100644
index 0000000..0be699c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/.spiceinit
@@ -0,0 +1,4 @@
+
+* user provided init file
+set ngbehavior=hs
+
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice
new file mode 100644
index 0000000..f381eff
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** nmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=0
+vgs G_tn 0 dc=3.3
+vbs S_tn 0 dc=0
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = {{width}}u L = {{length}}u nf={{nf}} ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vgs_min = -3.3
+let vgs_step = 0.1
+let vgs_max = 3.3
+
+compose vbs_vector start=0 stop=-3.3 step=-0.825
+
+set appendwrite
+
+foreach t 25
+
+ let vbs_counter = 0
+ while vbs_counter < length(vbs_vector)
+ option TEMP=25
+ alter vbs = vbs_vector[vbs_counter]
+
+ save @mn[vs] @mn[vgs] @mn[id] @mn[cgb]
+ *******************
+ ** simulation part
+ *******************
+ DC vgs $&vgs_min $&vgs_max $&vgs_step
+
+ * ** parameters calculation
+
+ print @mn[cgb]
+
+ wrdata nmos_3p3_cv/simulated_Cgc/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgb]*1e15}
+
+ reset
+ let vbs_counter = vbs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice
new file mode 100644
index 0000000..f7d8b08
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** pmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=0
+vgs G_tn 0 dc=3.3
+vbs S_tn 0 dc=0
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = {{width}}u L = {{length}}u nf={{nf}} ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vgs_min = -3.3
+let vgs_step = 0.1
+let vgs_max = 3.3
+
+compose vbs_vector start=0 stop=3.3 step=0.825
+
+set appendwrite
+
+foreach t 25
+
+ let vbs_counter = 0
+ while vbs_counter < length(vbs_vector)
+ option TEMP=25
+ alter vbs = vbs_vector[vbs_counter]
+
+ save @mn[vs] @mn[vgs] @mn[id] @mn[cgb]
+ *******************
+ ** simulation part
+ *******************
+ DC vgs $&vgs_min $&vgs_max $&vgs_step
+
+ * ** parameters calculation
+
+ print @mn[cgb]
+
+ wrdata pmos_3p3_cv/simulated_Cgc/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgb]*1e15}
+
+ reset
+ let vbs_counter = vbs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice
new file mode 100644
index 0000000..8d4ed65
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** nmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+vs S_tn 0 dc=0.2
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.1
+let vds_max = 3.3
+
+compose vgs_vector start=0 stop=3.3 step=1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgd]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgd]
+
+ wrdata nmos_3p3_cv/simulated_Cgd/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgd]*1e15}
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice
new file mode 100644
index 0000000..ae302a1
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** pmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+vs S_tn 0 dc=0.2
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.1
+let vds_max = -3.3
+
+compose vgs_vector start=0 stop=-3.3 step=-1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgd]
+
+ wrdata pmos_3p3_cv/simulated_Cgd/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgd]*1e15}
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice
new file mode 100644
index 0000000..6bb276f
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** nmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+vs S_tn 0 dc=0.1
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.1
+let vds_max = 3.3
+
+compose vgs_vector start=0 stop=3.3 step=1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgs]
+
+ wrdata nmos_3p3_cv/simulated_Cgs/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgs]*1e15}
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice
new file mode 100644
index 0000000..eaa97b0
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice
@@ -0,0 +1,74 @@
+***************************
+** pmos_3p3_cv
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+vs S_tn 0 dc=0.1
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.1
+let vds_max = -3.3
+
+compose vgs_vector start=0 stop=-3.3 step=-1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgs]
+
+ wrdata pmos_3p3_cv/simulated_Cgs/{{i}}_simulated_W{{width}}_L{{length}}.csv {@mn[cgs]*1e15}
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx b/models/ngspice/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..813635c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx b/models/ngspice/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..fd139e1
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6d80ee
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx
new file mode 100644
index 0000000..407129c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..1dbd85c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_cv/models_regression.py b/models/ngspice/testing/regression/mos_cv/models_regression.py
new file mode 100644
index 0000000..f4cdf84
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_cv/models_regression.py
@@ -0,0 +1,293 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,sweep_x,sweep_y,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Cgc
+ if sim_val == "Cgc":
+ col_list = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ else:
+ # measured Cgs & Cgd
+ if sim_val == "Cgs":
+ col_list = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ else:
+ col_list = [sweep_x,f"{sweep_y[0]}.1",f"{sweep_y[1]}.1",f"{sweep_y[2]}.1",f"{sweep_y[3]}.1"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ df_measured.to_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Cgc
+ if sim_val == "Cgc":
+ col_list = [sweep_x,f"{sweep_y[0]}.{i}",f"{sweep_y[1]}.{i}",f"{sweep_y[2]}.{i}",f"{sweep_y[3]}.{i}",f"{sweep_y[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,f"{sweep_y[0]}",f"{sweep_y[1]}",f"{sweep_y[2]}",f"{sweep_y[3]}",f"{sweep_y[4]}"]
+ else:
+ # measured Cgs & Cgd
+ cgs_index = 2*i
+ cgd_index = cgs_index + 1
+ if sim_val == "Cgs":
+ col_list = [sweep_x,f"{sweep_y[0]}.{cgs_index}",f"{sweep_y[1]}.{cgs_index}",f"{sweep_y[2]}.{cgs_index}",f"{sweep_y[3]}.{cgs_index}"]
+ else:
+ col_list = [sweep_x,f"{sweep_y[0]}.{cgd_index}",f"{sweep_y[1]}.{cgd_index}",f"{sweep_y[2]}.{cgd_index}",f"{sweep_y[3]}.{cgd_index}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,f"{sweep_y[0]}",f"{sweep_y[1]}",f"{sweep_y[2]}",f"{sweep_y[3]}"]
+
+ df_measured.to_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv", index = False)
+
+def ext_simulated(device,sweep_x,sweep_y,vds_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i == 0:
+ nf = 20
+ else:
+ nf = 1
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i, nf = nf ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+ # empty array to append in it shaped (vds_sweep, number of trials + 1)
+ new_array = np.empty((vds_sweep, 1+int(df_simulated.shape[0]/vds_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vds_sweep, 0]
+ times = int(df_simulated.shape[0]/vds_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vds_sweep:(j+1)*vds_sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ if sim_val == "Cgc":
+ df_simulated.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ else:
+ df_simulated.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,sweep_x,sweep_y,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * (abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1]),6)
+ error_2 = round (100 * (abs(measured.iloc[:, 2]) - abs(simulated.iloc[:, 2]))/abs(measured.iloc[:, 2]),6)
+ error_3 = round (100 * (abs(measured.iloc[:, 3]) - abs(simulated.iloc[:, 3]))/abs(measured.iloc[:, 3]),6)
+ error_4 = round (100 * (abs(measured.iloc[:, 4]) - abs(simulated.iloc[:, 4]))/abs(measured.iloc[:, 4]),6)
+ if sim_val == "Cgc":
+ error_5 = round (100 * (abs(measured.iloc[:, 5]) - abs(simulated.iloc[:, 5]))/abs(measured.iloc[:, 5]),6)
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ else:
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ if sim_val == "Cgc":
+ mean_error = (df_error[sweep_y[0]].mean() + df_error[sweep_y[1]].mean() + df_error[sweep_y[2]].mean() +
+ df_error[sweep_y[3]].mean() + df_error[sweep_y[4]].mean())/5
+ else:
+ mean_error = (df_error[sweep_y[0]].mean() + df_error[sweep_y[1]].mean() + df_error[sweep_y[2]].mean() +
+ df_error[sweep_y[3]].mean())/4
+ # Max error
+ if sim_val == "Cgc":
+ max_error = df_error[[sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]].max().max()
+ else:
+ max_error = df_error[[sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_sweep_y = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_sweep_x = df_error[sweep_x][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'25', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_sweep_y} & {sweep_x}= {max_location_sweep_x}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_cv" , "pmos_3p3_cv" ]
+ measured_data = ["3p3_cv"]
+ nmos_vds = "Vds (V)"
+ pmos_vds = "-Vds (V)"
+ nmos_vgs = "Vgs (V)"
+ pmos_vgs = "-Vgs (V)"
+ nmos_rds = "Rds"
+ cgc_sim = "Cgc"
+ cgs_sim = "Cgs"
+ cgd_sim = "Cgd"
+ Rds_sim = "Rds"
+ mos_3p3_vbs_sweep = 67
+ mos_3p3_vgs_sweep = 34
+
+ mos_6p0_vgs_sweep = 133
+
+ nmos3p3_vgs = ["Vgs=0" , "Vgs=1.1" , "Vgs=2.2" , "Vgs=3.3" ]
+ pmos3p3_vgs = ["Vgs=-0" , "Vgs=-1.1" , "Vgs=-2.2" , "Vgs=-3.3"]
+ # pmos3p3_vgs = [-0.8 , -1.3 , -1.8 , -2.3 , -2.8 , -3.3]
+ # nmos6p0_vgs = [ 1 , 2 , 3 , 4 , 5 , 6]
+ # pmos6p0_vgs = [-1 , -2 , -3 , -4 , -5 , -6]
+ # nmos6p0_nat_vgs = [ 0.25 , 1.4 , 2.55 , 3.7 , 4.85 , 6]
+
+ nmos3p3_vbs = ["Vbs=0" , "Vbs=-0.825" , "Vbs=-1.65" , "Vbs=-2.475" , "Vbs=-3.3"]
+ pmos3p3_vbs = ["Vbs=-0" , "Vbs=0.825" , "Vbs=1.65" , "Vbs=2.475" , "Vbs=3.3" ]
+ # nmos6p0_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+ # pmos6p0_vbs = [ 0 , 0.75 , 1.5 , 2.25 , 3]
+ # nmos6p0_nat_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{cgd_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"./measured_data/{measured_data[0]}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{cgd_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgd_sim}",exist_ok=False)
+
+
+ # =========== nmos_3p3_cv ==============
+ # Cgc
+ ext_measured ("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,cgc_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,cgc_sim)
+ error_cal ("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,cgc_sim)
+
+ # Cgs
+ ext_measured ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgs_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,cgs_sim)
+ error_cal ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgs_sim)
+
+ # Cgd
+ ext_measured ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgd_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,cgd_sim)
+ error_cal ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgd_sim)
+
+
+ # =========== nmos_3p3_cv ==============
+ # Cgc
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,cgc_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,mos_3p3_vbs_sweep,cgc_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,cgc_sim)
+
+ # Cgs
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgs_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,mos_3p3_vgs_sweep,cgs_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgs_sim)
+
+ # Cgd
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgd_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,mos_3p3_vgs_sweep,cgd_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgd_sim)
+
+ # =========== pmos_3p3_iv ==============
+
+
+ # =========== nmos_6p0_iv ==============
+
+
+ # =========== pmos_6p0_iv ==============
+
+
+ # ============ nmos_3p3_sab_iv ============= # Error in ngspice
+
+
+ # ============ nmos_6p0_nat_iv =============
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/.spiceinit b/models/ngspice/testing/regression/mos_iv_vbs/.spiceinit
new file mode 100644
index 0000000..0be699c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/.spiceinit
@@ -0,0 +1,4 @@
+
+* user provided init file
+set ngbehavior=hs
+
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv.spice
new file mode 100644
index 0000000..47ce6c1
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv.spice
@@ -0,0 +1,48 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* power supply
+Vds D_tn 0 dc=0.05
+Vgs G_tn 0 dc=3.3
+Vbs B_tn 0 dc=0
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 B_tn nmos_3p3 W = {{width}}u L = {{length}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vgs 0 3.3 0.05 Vbs 0 -3.3 -0.825
+print -i(Vds)
+wrdata nmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_iv.spice
new file mode 100644
index 0000000..0551cd7
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_iv.spice
@@ -0,0 +1,48 @@
+***************************
+** nmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* power supply
+Vds D_tn 0 dc=0.05
+Vgs G_tn 0 dc=6
+Vbs S_tn 0 dc=0
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 S_tn nmos_6p0 W = {{width}}u L = {{length}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vgs 0 6 0.05 Vbs 0 -3 -0.75
+print -i(Vds)
+wrdata nmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_nat_iv.spice b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_nat_iv.spice
new file mode 100644
index 0000000..3c50e5f
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_6p0_nat_iv.spice
@@ -0,0 +1,48 @@
+***************************
+** nmos_6p0_nat_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* power supply
+Vds D_tn 0 dc=0.05
+Vgs G_tn 0 dc=6
+Vbs S_tn 0 dc=0
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 S_tn nmos_6p0_nat W = {{width}}u L = {{length}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vgs -0.5 6 0.05 Vbs 0 -3 -0.75
+print -i(Vds)
+wrdata nmos_6p0_nat_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_3p3_iv.spice
new file mode 100644
index 0000000..5a4a87a
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_3p3_iv.spice
@@ -0,0 +1,48 @@
+***************************
+** pmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* power supply
+Vds D_tn 0 dc=-0.05
+Vgs G_tn 0 dc=-3.3
+Vbs S_tn 0 dc=0
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 S_tn pmos_3p3 W = {{width}}u L = {{length}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vgs 0 -3.3 -0.05 Vbs 0 3.3 0.825
+print -i(Vds)
+wrdata pmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_6p0_iv.spice
new file mode 100644
index 0000000..e2237f2
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/device_netlists_Id/pmos_6p0_iv.spice
@@ -0,0 +1,50 @@
+***************************
+** pmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* power supply
+Vds D_tn 0 dc=-0.05
+Vgs G_tn 0 dc=-6
+Vbs S_tn 0 dc=0
+
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 S_tn pmos_6p0 W = {{width}}u L = {{length}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vgs 0 -6 -0.05 Vbs 0 3 0.75
+print -i(Vds)
+wrdata pmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vbs/models_regression.py b/models/ngspice/testing/regression/mos_iv_vbs/models_regression.py
new file mode 100644
index 0000000..675229e
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vbs/models_regression.py
@@ -0,0 +1,237 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vgs,vbs):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops*2,2):
+ width = dimensions["W (um)"].iloc[int(i/2)]
+ length = dimensions["L (um)"].iloc[int(i/2)]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ if device in ["pmos_3p3_iv" , "pmos_6p0_iv"]:
+ col_list = ['-vgs ',f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ else:
+ col_list = ['vgs ',f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vgs}",f"vbs ={vbs[0]}.{i}",f"vbs ={vbs[1]}.{i}",f"vbs ={vbs[2]}.{i}",f"vbs ={vbs[3]}.{i}",f"vbs ={vbs[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+
+
+def ext_simulated(device,vgs,vbs,vbs_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ AD = float(width) * 0.24
+ PD = 2 * (float(width) + 0.24)
+ AS = AD
+ PS = PD
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else:
+ temp = 125
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i , temp = temp , AD = AD , PD = PD , AS = AS , PS = PD ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+ # empty array to append in it shaped (vbs_sweep, number of trials + 1)
+ new_array = np.empty((vbs_sweep, 1+int(df_simulated.shape[0]/vbs_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vbs_sweep, 0]
+ times = int(df_simulated.shape[0]/vbs_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vbs_sweep:(j+1)*vbs_sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ df_simulated.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,vgs,vbs,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else: temp = 125
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[:, 2]) - abs(simulated.iloc[:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[:, 3]) - abs(simulated.iloc[:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[:, 4]) - abs(simulated.iloc[:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[:, 5]) - abs(simulated.iloc[:, 5]))/abs(measured.iloc[:, 5])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"vbs ={vbs[0]}"].mean() + df_error[f"vbs ={vbs[1]}"].mean() + df_error[f"vbs ={vbs[2]}"].mean() +
+ df_error[f"vbs ={vbs[3]}"].mean() + df_error[f"vbs ={vbs[4]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vbs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vgs = df_error[f"{vgs}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vbs (V) = {max_location_vbs}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_iv" , "pmos_3p3_iv" , "nmos_6p0_iv" , "pmos_6p0_iv" , "nmos_6p0_nat_iv"] #"nmos_3p3_sab_iv"
+ nmos_vgs = "vgs (V)"
+ pmos_vgs = "-vgs (V)"
+ nmos_rds = "Rds"
+ Id_sim = "Id"
+ Rds_sim = "Rds"
+ mos_3p3_vbs_sweep = 67
+ mos_6p0_vbs_sweep = 121
+ mos_6p0_nat_vbs_sweep = 131
+ nmos3p3_vbs = [0 , -0.825 , -1.65 , -2.48 , -3.3]
+ pmos3p3_vbs = [0 , 0.825 , 1.65 , 2.48 , 3.3]
+ nmos6p0_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+ pmos6p0_vbs = [ 0 , 0.75 , 1.5 , 2.25 , 3]
+ nmos6p0_nat_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/measured_{Rds_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/MOS/{device}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/simulated_{Rds_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/error_{Rds_sim}",exist_ok=False)
+
+ # =========== nmos_3p3_iv ==============
+ ext_measured ("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs)
+ ext_simulated("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ error_cal ("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs,Id_sim)
+
+ # =========== pmos_3p3_iv ==============
+ ext_measured ("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs)
+ ext_simulated("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ error_cal ("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs,Id_sim)
+
+ # =========== nmos_6p0_iv ==============
+ ext_measured ("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs)
+ ext_simulated("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs,mos_6p0_vbs_sweep,Id_sim)
+ error_cal ("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs,Id_sim)
+
+ # =========== pmos_6p0_iv ==============
+ ext_measured ("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs)
+ ext_simulated("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs,mos_6p0_vbs_sweep,Id_sim)
+ error_cal ("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs,Id_sim)
+
+ # ============ nmos_3p3_sab_iv ============= # Error in ngspice
+ # ext_measured ("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs)
+ # ext_simulated("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ # error_cal ("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs,Rds_sim)
+
+ # ============ nmos_6p0_nat_iv =============
+ ext_measured ("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs)
+ ext_simulated("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs,mos_6p0_nat_vbs_sweep,Id_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs,Id_sim)
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
\ No newline at end of file
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/.spiceinit b/models/ngspice/testing/regression/mos_iv_vgs/.spiceinit
new file mode 100644
index 0000000..0be699c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/.spiceinit
@@ -0,0 +1,4 @@
+
+* user provided init file
+set ngbehavior=hs
+
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_W0.22_L0.5.ods b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_W0.22_L0.5.ods
new file mode 100644
index 0000000..9a12f21
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_W0.22_L0.5.ods
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_measured_W0.22_L0.5.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_measured_W0.22_L0.5.csv
new file mode 100644
index 0000000..b656307
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_measured_W0.22_L0.5.csv
@@ -0,0 +1,68 @@
+vds (V),vgs =0.8_mea,vgs =1.3_mea,vgs =1.8_mea,vgs =2.3_mea,vgs =2.8_mea,vgs =3.3_mea
+0,0,0,0,0,0,0
+0.05,0.000000394808,0.00000250744,0.00000415488,0.00000540081,0.00000635234,0.0000070844
+0.1,0.000000599289,0.00000469955,0.00000796984,0.0000104601,0.0000123724,0.0000138509
+0.15,0.000000675551,0.00000659522,0.0000114625,0.0000151936,0.0000180738,0.0000203114
+0.2,0.000000703823,0.00000821193,0.0000146493,0.000019616,0.0000234695,0.000026477
+0.25,0.000000719359,0.00000956596,0.0000175456,0.0000237412,0.0000285717,0.0000323583
+0.3,0.00000073047,0.0000106727,0.0000201655,0.0000275821,0.0000333918,0.0000379655
+0.35,0.000000739448,0.0000115474,0.0000225223,0.0000311509,0.000037941,0.0000433083
+0.4,0.000000747171,0.0000122074,0.000024628,0.0000344592,0.0000422294,0.0000483957
+0.45,0.000000754064,0.0000126791,0.0000264939,0.0000375176,0.0000462669,0.0000532366
+0.5,0.000000760366,0.0000130039,0.0000281296,0.000040336,0.0000500628,0.0000578394
+0.55,0.000000766223,0.0000132304,0.0000295432,0.0000429238,0.0000536257,0.0000622118
+0.6,0.000000771734,0.0000133967,0.0000307404,0.000045289,0.0000569639,0.0000663615
+0.65,0.000000776969,0.0000135258,0.000031726,0.0000474388,0.000060085,0.0000702956
+0.7,0.000000781977,0.000013631,0.0000325086,0.0000493786,0.0000629959,0.0000740209
+0.75,0.000000786795,0.0000137197,0.0000331103,0.0000511113,0.0000657028,0.0000775436
+0.8,0.000000791454,0.0000137967,0.0000335682,0.0000526365,0.000068211,0.0000808698
+0.85,0.000000795976,0.0000138648,0.0000339219,0.0000539506,0.0000705243,0.0000840048
+0.9,0.00000080038,0.0000139261,0.0000342032,0.0000550503,0.0000726444,0.0000869533
+0.95,0.00000080468,0.000013982,0.0000344339,0.0000559425,0.0000745699,0.0000897193
+1,0.000000808889,0.0000140335,0.0000346285,0.0000566516,0.0000762953,0.0000923055
+1.05,0.000000813017,0.0000140813,0.0000347963,0.0000572141,0.0000778115,0.0000947128
+1.1,0.000000817074,0.000014126,0.0000349439,0.0000576666,0.0000791096,0.0000969396
+1.15,0.000000821066,0.0000141682,0.0000350756,0.0000580386,0.0000801907,0.0000989809
+1.2,0.000000825,0.0000142081,0.0000351946,0.0000583513,0.0000810732,0.000100827
+1.25,0.000000828881,0.000014246,0.0000353034,0.0000586196,0.0000817895,0.000102465
+1.3,0.000000832715,0.0000142822,0.0000354036,0.000058854,0.0000823753,0.000103883
+1.35,0.000000836505,0.0000143169,0.0000354967,0.0000590619,0.0000828619,0.000105083
+1.4,0.000000840256,0.0000143502,0.0000355836,0.0000592486,0.0000832735,0.000106079
+1.45,0.00000084397,0.0000143823,0.0000356653,0.0000594181,0.0000836278,0.000106901
+1.5,0.000000847651,0.0000144134,0.0000357424,0.0000595734,0.0000839377,0.000107584
+1.55,0.000000851301,0.0000144434,0.0000358154,0.0000597167,0.0000842125,0.000108158
+1.6,0.000000854924,0.0000144725,0.000035885,0.0000598498,0.0000844591,0.000108648
+1.65,0.00000085852,0.0000145008,0.0000359513,0.0000599743,0.0000846827,0.000109072
+1.7,0.000000862094,0.0000145284,0.0000360149,0.0000600912,0.0000848871,0.000109444
+1.75,0.000000865647,0.0000145552,0.0000360759,0.0000602015,0.0000850756,0.000109776
+1.8,0.00000086918,0.0000145814,0.0000361346,0.000060306,0.0000852504,0.000110073
+1.85,0.000000872698,0.000014607,0.0000361912,0.0000604053,0.0000854135,0.000110344
+1.9,0.000000876202,0.0000146321,0.0000362458,0.0000605,0.0000855663,0.000110591
+1.95,0.000000879694,0.0000146567,0.0000362988,0.0000605906,0.0000857103,0.000110819
+2,0.000000883178,0.0000146808,0.0000363501,0.0000606774,0.0000858464,0.00011103
+2.05,0.000000886656,0.0000147045,0.0000364,0.0000607608,0.0000859756,0.000111227
+2.1,0.000000890133,0.0000147278,0.0000364485,0.0000608412,0.0000860985,0.000111412
+2.15,0.000000893611,0.0000147508,0.0000364957,0.0000609187,0.0000862159,0.000111586
+2.2,0.000000897095,0.0000147734,0.0000365418,0.0000609936,0.0000863281,0.000111751
+2.25,0.000000900589,0.0000147958,0.0000365868,0.0000610661,0.0000864358,0.000111907
+2.3,0.000000904098,0.000014818,0.0000366308,0.0000611364,0.0000865394,0.000112055
+2.35,0.000000907628,0.00001484,0.000036674,0.0000612047,0.0000866391,0.000112196
+2.4,0.000000911185,0.0000148619,0.0000367164,0.0000612711,0.0000867353,0.000112331
+2.45,0.000000914776,0.0000148837,0.0000367581,0.0000613359,0.0000868283,0.000112461
+2.5,0.000000918408,0.0000149056,0.0000367992,0.0000613991,0.0000869184,0.000112585
+2.55,0.000000922089,0.0000149275,0.0000368398,0.0000614608,0.0000870057,0.000112705
+2.6,0.000000925828,0.0000149496,0.0000368801,0.0000615213,0.0000870905,0.000112821
+2.65,0.000000929635,0.0000149719,0.0000369202,0.0000615808,0.0000871731,0.000112932
+2.7,0.000000933521,0.0000149945,0.0000369601,0.0000616392,0.0000872535,0.00011304
+2.75,0.000000937496,0.0000150176,0.0000370002,0.0000616969,0.0000873321,0.000113145
+2.8,0.000000941573,0.0000150412,0.0000370404,0.000061754,0.000087409,0.000113246
+2.85,0.000000945765,0.0000150655,0.0000370811,0.0000618108,0.0000874844,0.000113345
+2.9,0.000000950086,0.0000150906,0.0000371225,0.0000618673,0.0000875586,0.000113441
+2.95,0.000000954551,0.0000151167,0.0000371646,0.000061924,0.0000876318,0.000113535
+3,0.000000959177,0.0000151438,0.000037208,0.0000619811,0.0000877043,0.000113627
+3.05,0.00000096398,0.0000151723,0.0000372527,0.0000620388,0.0000877763,0.000113717
+3.1,0.00000096898,0.0000152021,0.0000372991,0.0000620976,0.0000878482,0.000113805
+3.15,0.000000974195,0.0000152337,0.0000373475,0.0000621577,0.0000879203,0.000113893
+3.2,0.000000979646,0.0000152671,0.0000373982,0.0000622196,0.000087993,0.000113979
+3.25,0.000000985354,0.0000153025,0.0000374518,0.0000622838,0.0000880667,0.000114066
+3.3,0.000000991344,0.0000153403,0.0000375085,0.0000623507,0.000088142,0.000114152
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_simulated_W0.22_L0.5.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_simulated_W0.22_L0.5.csv
new file mode 100644
index 0000000..559bf1d
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/48_W0.22_L0.5_id/48_simulated_W0.22_L0.5.csv
@@ -0,0 +1,68 @@
+vds (V),vgs =0.8,vgs =1.3,vgs =1.8,vgs =2.3,vgs =2.8,vgs =3.3
+0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0
+0.05,3.22975818e-07,2.43157729e-06,4.09284118e-06,5.34817009e-06,6.3062549e-06,7.04303691e-06
+0.1,4.73071201e-07,4.55021762e-06,7.84708646e-06,1.03557458e-05,1.22809248e-05,1.37688149e-05
+0.15,5.22055208e-07,6.374768e-06,1.1280361e-05,1.50384049e-05,1.79376507e-05,2.01890947e-05
+0.2,5.40679521e-07,7.92268471e-06,1.44090676e-05,1.9410865e-05,2.32893288e-05,2.6315059e-05
+0.25,5.51695359e-07,9.21026143e-06,1.72484754e-05,2.348695e-05,2.83481576e-05,3.21573459e-05
+0.3,5.59915347e-07,1.02530145e-05,1.9812787e-05,2.72796465e-05,3.31256801e-05,3.77260797e-05
+0.35,5.66722321e-07,1.10666892e-05,2.21151771e-05,3.08011505e-05,3.76328221e-05,4.30308996e-05
+0.4,5.72672907e-07,1.16704247e-05,2.41677853e-05,3.40629013e-05,4.18799238e-05,4.80809843e-05
+0.45,5.78045815e-07,1.20941843e-05,2.59816266e-05,3.70755973e-05,4.58767664e-05,5.2885075e-05
+0.5,5.83001365e-07,1.23833907e-05,2.75663671e-05,3.98491846e-05,4.963259e-05,5.74514943e-05
+0.55,5.87640754e-07,1.25858737e-05,2.89299281e-05,4.23928002e-05,5.31561019e-05,6.17881613e-05
+0.6,5.92032203e-07,1.27359725e-05,3.00781939e-05,4.47146361e-05,5.64554683e-05,6.59026025e-05
+0.65,5.96224007e-07,1.28537477e-05,3.10163684e-05,4.68216674e-05,5.95382827e-05,6.98019558e-05
+0.7,6.00251662e-07,1.29504451e-05,3.17551904e-05,4.87191541e-05,6.24114941e-05,7.34929653e-05
+0.75,6.04142056e-07,1.30325963e-05,3.23200019e-05,5.04098419e-05,6.508127e-05,7.69819631e-05
+0.8,6.07916057e-07,1.31042102e-05,3.27494245e-05,5.18930755e-05,6.75527479e-05,8.02748297e-05
+0.85,6.11590195e-07,1.31678835e-05,3.30822055e-05,5.31651395e-05,6.98296037e-05,8.33769212e-05
+0.9,6.15177791e-07,1.32253787e-05,3.33480736e-05,5.42239257e-05,7.19133463e-05,8.62929434e-05
+0.95,6.18689737e-07,1.32779404e-05,3.35671619e-05,5.5078809e-05,7.38023091e-05,8.90267371e-05
+1.0,6.22135057e-07,1.33264769e-05,3.37526468e-05,5.57566042e-05,7.54907048e-05,9.15809225e-05
+1.05,6.25521309e-07,1.33716705e-05,3.3913231e-05,5.6294539e-05,7.69692357e-05,9.39563273e-05
+1.1,6.28854889e-07,1.34140451e-05,3.40548112e-05,5.67282911e-05,7.82300418e-05,9.61511324e-05
+1.15,6.32141255e-07,1.3454012e-05,3.41815036e-05,5.70858708e-05,7.92762668e-05,9.81598107e-05
+1.2,6.35385106e-07,1.34918993e-05,3.42962667e-05,5.73873704e-05,8.01284864e-05,9.99724573e-05
+1.25,6.38590517e-07,1.35279731e-05,3.44012836e-05,5.76468022e-05,8.08200577e-05,0.000101576191
+1.3,6.41761046e-07,1.35624516e-05,3.44982045e-05,5.78739646e-05,8.13864272e-05,0.000102960854
+1.35,6.44899828e-07,1.35955165e-05,3.45883026e-05,5.807582e-05,8.18578436e-05,0.00010412798
+1.4,6.48009642e-07,1.36273203e-05,3.46725789e-05,5.825742e-05,8.22574483e-05,0.000105095839
+1.45,6.51092978e-07,1.36579922e-05,3.47518332e-05,5.84225099e-05,8.26021608e-05,0.000105895033
+1.5,6.54152089e-07,1.36876428e-05,3.48267139e-05,5.85739247e-05,8.29042181e-05,0.000106559196
+1.55,6.5718904e-07,1.37163674e-05,3.48977531e-05,5.87138513e-05,8.31725263e-05,0.000107118221
+1.6,6.60205754e-07,1.37442488e-05,3.49653929e-05,5.88440069e-05,8.3413658e-05,0.000107595914
+1.65,6.63204051e-07,1.37713594e-05,3.50300041e-05,5.89657603e-05,8.36325464e-05,0.000108010297
+1.7,6.66185693e-07,1.37977631e-05,3.50919009e-05,5.90802185e-05,8.38329589e-05,0.000108374781
+1.75,6.69152418e-07,1.38235168e-05,3.51513515e-05,5.91882878e-05,8.40178207e-05,0.000108699352
+1.8,6.72105985e-07,1.38486714e-05,3.52085868e-05,5.92907191e-05,8.41894393e-05,0.000108991508
+1.85,6.75048205e-07,1.38732733e-05,3.52638071e-05,5.93881403e-05,8.43496608e-05,0.000109256959
+1.9,6.77980985e-07,1.38973652e-05,3.53171876e-05,5.9481082e-05,8.4499982e-05,0.000109500113
+1.95,6.80906363e-07,1.39209872e-05,3.53688826e-05,5.9569996e-05,8.46416309e-05,0.000109724423
+2.0,6.83826546e-07,1.39441772e-05,3.54190291e-05,5.96552703e-05,8.47756258e-05,0.000109932633
+2.05,6.8674395e-07,1.39669725e-05,3.54677501e-05,5.97372405e-05,8.49028197e-05,0.00011012695
+2.1,6.89661232e-07,1.39894098e-05,3.55151576e-05,5.98161993e-05,8.50239335e-05,0.000110309173
+2.15,6.92581334e-07,1.40115264e-05,3.55613544e-05,5.98924037e-05,8.51395817e-05,0.000110480783
+2.2,6.95507514e-07,1.40333611e-05,3.56064371e-05,5.99660814e-05,8.52502917e-05,0.000110643012
+2.25,6.98443385e-07,1.40549546e-05,3.56504979e-05,6.00374362e-05,8.53565205e-05,0.000110796896
+2.3,7.01392952e-07,1.40763505e-05,3.56936266e-05,6.01066527e-05,8.54586659e-05,0.000110943311
+2.35,7.04360645e-07,1.40975957e-05,3.57359129e-05,6.01739001e-05,8.55570779e-05,0.000111083007
+2.4,7.07351354e-07,1.41187416e-05,3.57774479e-05,6.02393365e-05,8.56520665e-05,0.000111216627
+2.45,7.10370467e-07,1.41398444e-05,3.58183266e-05,6.03031122e-05,8.57439093e-05,0.000111344731
+2.5,7.13423899e-07,1.41609658e-05,3.5858649e-05,6.03653733e-05,8.58328577e-05,0.000111467806
+2.55,7.16518129e-07,1.4182174e-05,3.58985226e-05,6.04262647e-05,8.59191429e-05,0.000111586281
+2.6,7.1966023e-07,1.42035438e-05,3.59380641e-05,6.04859337e-05,8.60029808e-05,0.000111700538
+2.65,7.22857903e-07,1.42251578e-05,3.59774005e-05,6.05445331e-05,8.60845775e-05,0.000111810918
+2.7,7.26119505e-07,1.42471066e-05,3.60166719e-05,6.06022241e-05,8.61641334e-05,0.000111917733
+2.75,7.29454081e-07,1.42694895e-05,3.60560323e-05,6.06591795e-05,8.62418485e-05,0.000112021269
+2.8,7.32871395e-07,1.42924152e-05,3.60956516e-05,6.07155868e-05,8.63179265e-05,0.000112121795
+2.85,7.36381953e-07,1.4316002e-05,3.61357172e-05,6.07716507e-05,8.63925793e-05,0.00011221957
+2.9,7.39997036e-07,1.43403788e-05,3.61764354e-05,6.08275968e-05,8.64660314e-05,0.000112314846
+2.95,7.43728719e-07,1.43656853e-05,3.6218033e-05,6.08836737e-05,8.65385243e-05,0.000112407877
+3.0,7.47589903e-07,1.43920724e-05,3.62607589e-05,6.0940156e-05,8.66103205e-05,0.000112498923
+3.05,7.51594332e-07,1.44197029e-05,3.6304885e-05,6.09973468e-05,8.66817079e-05,0.000112588255
+3.1,7.55756619e-07,1.44487519e-05,3.63507081e-05,6.10555808e-05,8.67530037e-05,0.000112676164
+3.15,7.60092264e-07,1.44794069e-05,3.63985508e-05,6.11152262e-05,8.68245585e-05,0.000112762962
+3.2,7.64617674e-07,1.45118686e-05,3.64487628e-05,6.11766874e-05,8.68967599e-05,0.000112848992
+3.25,7.69350179e-07,1.4546351e-05,3.65017222e-05,6.12404072e-05,8.69700366e-05,0.000112934629
+3.3,7.74308048e-07,1.45830818e-05,3.65578365e-05,6.13068695e-05,8.70448619e-05,0.000113020288
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_W0.22_L0.28.ods b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_W0.22_L0.28.ods
new file mode 100644
index 0000000..46c102b
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_W0.22_L0.28.ods
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_measured_W0.22_L0.28.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_measured_W0.22_L0.28.csv
new file mode 100644
index 0000000..7a22f99
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_measured_W0.22_L0.28.csv
@@ -0,0 +1,80 @@
+vds (V),vgs =0.8,vgs =1.3,vgs =1.8,vgs =2.3,vgs =2.8,vgs =3.3
+0.0,0.0,0.0,0.0,0.0,0.0,0.0
+0.05,9.63741e-07,4.4867e-06,6.8598e-06,8.50294e-06,9.69135e-06,1.05788e-05
+0.1,1.53924e-06,8.34359e-06,1.30622e-05,1.63683e-05,1.878e-05,2.05934e-05
+0.15,1.83889e-06,1.16297e-05,1.8658e-05,2.36382e-05,2.73005e-05,3.00724e-05
+0.2,1.99847e-06,1.43967e-05,2.36926e-05,3.03509e-05,3.5285e-05,3.90427e-05
+0.25,2.10372e-06,1.66898e-05,2.82063e-05,3.65412e-05,4.27629e-05,4.75291e-05
+0.3,2.18687e-06,1.8549e-05,3.22351e-05,4.22407e-05,4.97614e-05,5.5555e-05
+0.35,2.25925e-06,2.00126e-05,3.58101e-05,4.74783e-05,5.63057e-05,6.31421e-05
+0.4,2.32554e-06,2.11271e-05,3.89574e-05,5.22795e-05,6.24188e-05,7.03105e-05
+0.45,2.38801e-06,2.19584e-05,4.16972e-05,5.66669e-05,6.81219e-05,7.70792e-05
+0.5,2.44795e-06,2.25849e-05,4.40432e-05,6.06592e-05,7.3434e-05,8.34656e-05
+0.55,2.50611e-06,2.30746e-05,4.6006e-05,6.42711e-05,7.83719e-05,8.94857e-05
+0.6,2.56302e-06,2.34745e-05,4.7604e-05,6.7511e-05,8.29498e-05,9.5154e-05
+0.65,2.61901e-06,2.38141e-05,4.88771e-05,7.03809e-05,8.7179e-05,0.000100484
+0.7,2.67433e-06,2.41117e-05,4.98869e-05,7.28776e-05,9.10661e-05,0.000105486
+0.75,2.72917e-06,2.43789e-05,5.06992e-05,7.49994e-05,9.46128e-05,0.000110169
+0.8,2.78366e-06,2.46232e-05,5.13687e-05,7.67607e-05,9.78138e-05,0.000114539
+0.85,2.8379e-06,2.48499e-05,5.19356e-05,7.82005e-05,0.000100658,0.000118598
+0.9,2.89199e-06,2.50627e-05,5.24274e-05,7.93761e-05,0.000103136,0.000122343
+0.95,2.94599e-06,2.52642e-05,5.2863e-05,8.03467e-05,0.000105248,0.000125764
+1.0,2.99996e-06,2.54564e-05,5.32556e-05,8.11628e-05,0.000107016,0.000128846
+1.05,3.05394e-06,2.56408e-05,5.36145e-05,8.1863e-05,0.000108485,0.000131575
+1.1,3.10797e-06,2.58187e-05,5.39464e-05,8.24752e-05,0.000109709,0.000133942
+1.15,3.16209e-06,2.5991e-05,5.42564e-05,8.30196e-05,0.000110742,0.000135961
+1.2,3.2163e-06,2.61584e-05,5.45482e-05,8.35109e-05,0.000111626,0.000137665
+1.25,3.27065e-06,2.63216e-05,5.48247e-05,8.39596e-05,0.000112396,0.000139101
+1.3,3.32515e-06,2.6481e-05,5.50883e-05,8.43738e-05,0.000113077,0.000140319
+1.35,3.37982e-06,2.66372e-05,5.53407e-05,8.47594e-05,0.000113687,0.000141365
+1.4,3.43466e-06,2.67904e-05,5.55835e-05,8.51213e-05,0.00011424,0.000142275
+1.45,3.4897e-06,2.6941e-05,5.58178e-05,8.5463e-05,0.000114747,0.000143078
+1.5,3.54494e-06,2.70892e-05,5.60447e-05,8.57874e-05,0.000115216,0.000143794
+1.55,3.6004e-06,2.72353e-05,5.6265e-05,8.60969e-05,0.000115654,0.000144442
+1.6,3.65608e-06,2.73795e-05,5.64794e-05,8.63934e-05,0.000116064,0.000145034
+1.65,3.71199e-06,2.75219e-05,5.66884e-05,8.66785e-05,0.000116452,0.000145579
+1.7,3.76815e-06,2.76627e-05,5.68928e-05,8.69534e-05,0.00011682,0.000146085
+1.75,3.82455e-06,2.7802e-05,5.70927e-05,8.72194e-05,0.000117171,0.000146558
+1.8,3.88121e-06,2.794e-05,5.72888e-05,8.74773e-05,0.000117506,0.000147003
+1.85,3.93813e-06,2.80767e-05,5.74813e-05,8.77279e-05,0.000117829,0.000147424
+1.9,3.99533e-06,2.82124e-05,5.76704e-05,8.7972e-05,0.000118139,0.000147823
+1.95,4.05281e-06,2.83469e-05,5.78566e-05,8.82101e-05,0.000118439,0.000148204
+2.0,4.11059e-06,2.84806e-05,5.804e-05,8.84428e-05,0.000118729,0.000148569
+2.05,4.16867e-06,2.86134e-05,5.82208e-05,8.86706e-05,0.000119011,0.000148919
+2.1,4.22709e-06,2.87454e-05,5.83992e-05,8.88937e-05,0.000119285,0.000149256
+2.15,4.28585e-06,2.88768e-05,5.85756e-05,8.91127e-05,0.000119552,0.000149582
+2.2,4.34497e-06,2.90076e-05,5.87499e-05,8.93278e-05,0.000119812,0.000149897
+2.25,4.40448e-06,2.9138e-05,5.89225e-05,8.95394e-05,0.000120066,0.000150203
+2.3,4.46441e-06,2.9268e-05,5.90935e-05,8.97478e-05,0.000120315,0.0001505
+2.35,4.52479e-06,2.93978e-05,5.92631e-05,8.99532e-05,0.000120559,0.000150789
+2.4,4.58566e-06,2.95275e-05,5.94315e-05,9.01558e-05,0.000120798,0.000151071
+2.45,4.64706e-06,2.96573e-05,5.95989e-05,9.03561e-05,0.000121033,0.000151346
+2.5,4.70903e-06,2.97874e-05,5.97655e-05,9.05542e-05,0.000121264,0.000151615
+2.55,4.77164e-06,2.99179e-05,5.99317e-05,9.07505e-05,0.000121492,0.000151879
+2.6,4.83494e-06,3.0049e-05,6.00976e-05,9.09452e-05,0.000121716,0.000152137
+2.65,4.899e-06,3.0181e-05,6.02637e-05,9.11387e-05,0.000121938,0.000152391
+2.7,4.9639e-06,3.03142e-05,6.04302e-05,9.13314e-05,0.000122157,0.00015264
+2.75,5.02973e-06,3.04488e-05,6.05975e-05,9.15236e-05,0.000122374,0.000152886
+2.8,5.09657e-06,3.05852e-05,6.07661e-05,9.17159e-05,0.000122589,0.000153128
+2.85,5.16454e-06,3.07237e-05,6.09364e-05,9.19086e-05,0.000122804,0.000153367
+2.9,5.23375e-06,3.08646e-05,6.1109e-05,9.21025e-05,0.000123018,0.000153604
+2.95,5.30433e-06,3.10086e-05,6.12845e-05,9.2298e-05,0.000123232,0.000153838
+3.0,5.37641e-06,3.11559e-05,6.14634e-05,9.24958e-05,0.000123446,0.000154072
+3.05,5.45016e-06,3.13072e-05,6.16466e-05,9.26968e-05,0.000123662,0.000154305
+3.1,5.52572e-06,3.14629e-05,6.18347e-05,9.29017e-05,0.00012388,0.000154537
+3.15,5.6033e-06,3.16238e-05,6.20287e-05,9.31115e-05,0.0001241,0.000154771
+3.2,5.68307e-06,3.17904e-05,6.22294e-05,9.33272e-05,0.000124325,0.000155006
+3.25,5.76525e-06,3.19634e-05,6.24379e-05,9.355e-05,0.000124555,0.000155243
+3.3,5.85006e-06,3.21437e-05,6.26553e-05,9.37809e-05,0.000124791,0.000155484
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_simulated_W0.22_L0.28.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_simulated_W0.22_L0.28.csv
new file mode 100644
index 0000000..0b15593
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Id/49_W0.22_L0.28_id/49_simulated_W0.22_L0.28.csv
@@ -0,0 +1,68 @@
+vds (V),vgs =0.8,vgs =1.3,vgs =1.8,vgs =2.3,vgs =2.8,vgs =3.3
+0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0
+0.05,7.45110275e-07,4.30992195e-06,6.73573635e-06,8.40973068e-06,9.61747374e-06,1.05177665e-05
+0.1,1.14787859e-06,7.99557834e-06,1.28149982e-05,1.61814243e-05,1.86313228e-05,2.04703056e-05
+0.15,1.3378652e-06,1.1116265e-05,1.82889138e-05,2.33574517e-05,2.70764632e-05,2.9886548e-05
+0.2,1.44062798e-06,1.37238871e-05,2.32031543e-05,2.9976375e-05,3.49850972e-05,3.87934431e-05
+0.25,1.51339311e-06,1.58640641e-05,2.75984142e-05,3.60732842e-05,4.23869381e-05,4.72161098e-05
+0.3,1.57398124e-06,1.75778465e-05,3.15106613e-05,4.16800483e-05,4.93093966e-05,5.51779678e-05
+0.35,1.62850042e-06,1.89066912e-05,3.49710733e-05,4.68254625e-05,5.57777249e-05,6.27008481e-05
+0.4,1.67954465e-06,1.99038903e-05,3.80055631e-05,5.1535257e-05,6.18151125e-05,6.98050819e-05
+0.45,1.72842228e-06,2.06426046e-05,4.06339946e-05,5.58319e-05,6.74427175e-05,7.65095639e-05
+0.5,1.77587873e-06,2.12014099e-05,4.28702151e-05,5.97340983e-05,7.26796093e-05,8.28317845e-05
+0.55,1.82237721e-06,2.1642574e-05,4.47264474e-05,6.32558909e-05,7.75425798e-05,8.87878199e-05
+0.6,1.86822461e-06,2.20069087e-05,4.62257015e-05,6.64053963e-05,8.20457579e-05,9.43922632e-05
+0.65,1.91363432e-06,2.23194529e-05,4.74142846e-05,6.91840043e-05,8.61999379e-05,9.96580688e-05
+0.7,1.95876051e-06,2.25956399e-05,4.83568809e-05,7.15885247e-05,9.00115486e-05,0.000104596268
+0.75,2.00371796e-06,2.28452948e-05,4.91178383e-05,7.36200066e-05,9.34813539e-05,0.000109215497
+0.8,2.04859432e-06,2.30749214e-05,4.97484587e-05,7.52976946e-05,9.66035673e-05,0.000113521265
+0.85,2.0934579e-06,2.32889901e-05,5.02853103e-05,7.66655974e-05,9.93673135e-05,0.000117514918
+0.9,2.13836285e-06,2.34906799e-05,5.07534419e-05,7.77828289e-05,0.000101763357,0.000121192432
+0.95,2.18335277e-06,2.36823187e-05,5.11699862e-05,7.87076679e-05,0.000103796107,0.000124543646
+1.0,2.22846319e-06,2.38656557e-05,5.15468482e-05,7.9488099e-05,0.00010549299,0.000127553489
+1.05,2.27372338e-06,2.40420348e-05,5.18924873e-05,8.01601527e-05,0.000106901615,0.000130207452
+1.1,2.3191577e-06,2.42125084e-05,5.22130572e-05,8.07498866e-05,0.000108077159,0.000132501641
+1.15,2.36478658e-06,2.43779148e-05,5.25131378e-05,8.12760102e-05,0.000109070698,0.000134452166
+1.2,2.41062728e-06,2.45389316e-05,5.27962094e-05,8.17520636e-05,0.000109923908,0.000136095888
+1.25,2.45669449e-06,2.46961139e-05,5.30649672e-05,8.21879924e-05,0.000110668687,0.000137481615
+1.3,2.50300078e-06,2.48499215e-05,5.33215347e-05,8.25912293e-05,0.000111328816,0.000138659044
+1.35,2.54955699e-06,2.50007396e-05,5.35676102e-05,8.29674278e-05,0.000111921934,0.000139671729
+1.4,2.59637251e-06,2.51488935e-05,5.38045708e-05,8.33209617e-05,0.000112461204,0.000140554796
+1.45,2.64345558e-06,2.52946608e-05,5.4033547e-05,8.36552685e-05,0.00011295656,0.000141335427
+1.5,2.69081349e-06,2.54382796e-05,5.42554768e-05,8.39730892e-05,0.000113415606,0.000142034314
+1.55,2.73845281e-06,2.55799564e-05,5.44711468e-05,8.42766391e-05,0.000113844245,0.000142667182
+1.6,2.78637955e-06,2.5719871e-05,5.46812222e-05,8.45677299e-05,0.000114247124,0.000143246049
+1.65,2.83459941e-06,2.58581814e-05,5.48862702e-05,8.48478595e-05,0.000114627951,0.000143780191
+1.7,2.88311792e-06,2.59950275e-05,5.50867787e-05,8.51182792e-05,0.000114989723,0.000144276852
+1.75,2.93194068e-06,2.61305343e-05,5.528317e-05,8.53800437e-05,0.000115334888,0.000144741758
+1.8,2.98107354e-06,2.62648146e-05,5.54758125e-05,8.56340492e-05,0.000115665469,0.000145179489
+1.85,3.03052283e-06,2.63979718e-05,5.56650299e-05,8.58810636e-05,0.000115983152,0.00014559375
+1.9,3.08029561e-06,2.65301018e-05,5.58511092e-05,8.61217496e-05,0.000116289356,0.000145987574
+1.95,3.13039987e-06,2.66612952e-05,5.6034307e-05,8.63566832e-05,0.000116585288,0.000146363463
+2.0,3.18084485e-06,2.67916396e-05,5.62148553e-05,8.65863689e-05,0.000116871976,0.000146723509
+2.05,3.2316413e-06,2.69212213e-05,5.63929669e-05,8.68112519e-05,0.00011715031,0.000147069466
+2.1,3.28280177e-06,2.70501274e-05,5.65688399e-05,8.70317288e-05,0.000117421058,0.000147402828
+2.15,3.33434093e-06,2.71784476e-05,5.67426619e-05,8.72481566e-05,0.000117684894,0.000147724866
+2.2,3.38627591e-06,2.73062767e-05,5.69146148e-05,8.74608605e-05,0.000117942412,0.000148036679
+2.25,3.43862669e-06,2.74337157e-05,5.70848782e-05,8.76701415e-05,0.000118194138,0.000148339217
+2.3,3.4914164e-06,2.75608746e-05,5.72536342e-05,8.78762831e-05,0.000118440545,0.00014863331
+2.35,3.5446718e-06,2.76878738e-05,5.74210707e-05,8.80795576e-05,0.000118682063,0.000148919688
+2.4,3.59842363e-06,2.78148467e-05,5.7587386e-05,8.82802325e-05,0.000118919089,0.000149198999
+2.45,3.65270708e-06,2.79419415e-05,5.77527922e-05,8.84785765e-05,0.000119151994,0.000149471826
+2.5,3.70756225e-06,2.80693231e-05,5.79175199e-05,8.86748659e-05,0.000119381136,0.000149738694
+2.55,3.76303459e-06,2.81971756e-05,5.80818216e-05,8.88693898e-05,0.000119606863,0.00015000009
+2.6,3.81917542e-06,2.83257044e-05,5.82459762e-05,8.90624572e-05,0.000119829524,0.000150256468
+2.65,3.87604245e-06,2.84551382e-05,5.84102929e-05,8.9254402e-05,0.000120049479,0.000150508262
+2.7,3.93370027e-06,2.85857312e-05,5.85751155e-05,8.94455898e-05,0.0001202671,0.000150755899
+2.75,3.99222094e-06,2.87177657e-05,5.87408264e-05,8.96364235e-05,0.000120482788,0.000150999802
+2.8,4.0516845e-06,2.88515539e-05,5.89078507e-05,8.98273498e-05,0.000120696975,0.000151240407
+2.85,4.1121796e-06,2.89874402e-05,5.90766605e-05,9.0018865e-05,0.000120910132,0.000151478169
+2.9,4.17380404e-06,2.91258039e-05,5.92477793e-05,9.02115214e-05,0.000121122781,0.000151713572
+2.95,4.23666538e-06,2.92670608e-05,5.94217857e-05,9.04059331e-05,0.000121335499,0.00015194714
+3.0,4.30088155e-06,2.94116661e-05,5.95993182e-05,9.06027828e-05,0.000121548927,0.000152179448
+3.05,4.36658147e-06,2.95601159e-05,5.97810787e-05,9.08028275e-05,0.000121763783,0.000152411129
+3.1,4.43390564e-06,2.97129502e-05,5.99678375e-05,9.10069049e-05,0.00012198086,0.000152642885
+3.15,4.50300679e-06,2.98707544e-05,6.01604364e-05,9.12159393e-05,0.000122201046,0.0001528755
+3.2,4.57405047e-06,3.00341618e-05,6.03597938e-05,9.14309482e-05,0.000122425323,0.000153109844
+3.25,4.64721571e-06,3.02038554e-05,6.05669078e-05,9.1653048e-05,0.00012265478,0.000153346889
+3.3,4.7226956e-06,3.03805702e-05,6.07828606e-05,9.18834602e-05,0.000122890617,0.000153587718
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Rds/0_W10_L10.0_rds/0_measured_W10_L10.0.ods b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Rds/0_W10_L10.0_rds/0_measured_W10_L10.0.ods
new file mode 100644
index 0000000..e64e374
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/nmos_3p3/Rds/0_W10_L10.0_rds/0_measured_W10_L10.0.ods
Binary files differ
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_measured_W10_L10.0.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_measured_W10_L10.0.csv
new file mode 100644
index 0000000..69951e8
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_measured_W10_L10.0.csv
@@ -0,0 +1,80 @@
+-vds (V),vgs =-0.8,vgs =-1.3,vgs =-1.8,vgs =-2.3,vgs =-2.8,vgs =-3.3
+0.0,848229.659876871,56660.819270118,29459.593221936797,20736.304707555897,16511.458952513043,14059.991170325546
+0.05,1401830.510280324,61082.13103338749,30568.51320865456,21240.757615342627,16803.729083558224,14253.074744549269
+0.1,7056963.811889571,72354.39762793342,33056.21208865676,22326.76107909701,17420.237089426788,14655.42888379857
+0.15,44992351.300279,88628.12524926661,35981.96583872164,23529.02422783625,18083.37884317009,15080.977307653446
+0.2,162364020.1331407,113973.10234784594,39472.3338412107,24867.332779620723,18798.582586872948,15532.050385971452
+0.25,374391613.6278494,157803.3769922676,43707.05781569608,26366.44079415719,19572.535817740547,16010.502889895766
+0.3,661375661.3756175,244654.30346919785,48949.78241821715,28055.999775552,20412.329046744228,16518.823199035298
+0.35,986193293.8855648,443577.0049680621,55604.054647664925,29976.01918465229,21326.96368018085,17060.770464394176
+0.4,1314060446.7805707,947059.380623165,64313.64478287711,32177.10277366628,22326.91062537678,17639.173075566225
+0.45,1623376623.3767354,2196836.5553602832,76167.26331022922,34724.63365511494,23424.689622862497,18257.503834075804
+0.5,1904761904.7621465,4830917.874396219,93118.53990129438,37705.96885486972,24635.396137169893,18920.7598577159
+0.55,2150537634.408612,9319664.49207852,118934.34823977156,41242.21553181835,25977.399662293792,19633.6363457876
+0.6,2364066193.8535576,15698587.127158364,161420.50040355127,45502.11584838692,27473.28223302838,20401.91778027134
+0.65,2557544757.033464,23584905.660376523,237473.28425552117,50727.94602546544,29149.41992654348,21232.324090194907
+0.7,2724795640.326047,32573289.902279697,383435.5828220869,57290.17473503297,31042.403923759823,22133.197583054814
+0.75,2873563218.390609,42016806.7226882,672494.9562878325,65746.21959237344,33197.224712014126,23112.836869597395
+0.8,3003003003.0041327,51546391.75257231,1225490.1960784153,77000.07700007688,35670.97096382961,24182.627200619067
+0.85,3125000000.0003333,60975609.75609926,2178649.237472733,92609.74254491576,38538.61569292424,25356.255388204256
+0.9,3225806451.611999,69930069.9300864,3623188.405797162,115313.65313653168,41904.12336573916,26647.48048072053
+0.95,3322259136.2126546,78740157.48032084,5555555.555555921,150217.8158329579,45903.14436538903,28077.2686433064
+1.0,3412969283.2769003,86956521.73913199,7874015.748032093,206996.48105982124,50727.94602546549,29669.18854769327
+1.05,3496503496.5037417,94339622.6415062,10526315.789473137,303674.4609778317,56666.85555618523,31449.507815202727
+1.1,3571428571.427893,102040816.32649194,13333333.333331184,471698.1132075526,64131.34098634006,33454.88608611281
+1.15,3636363636.363009,108695652.17391476,16129032.258064386,757575.7575757483,73757.19132615431,35732.15179018071
+1.2,3717472118.959285,116279069.76743896,19230769.23077093,1213592.2330096934,86557.60408551907,38339.14810412916
+1.25,3773584905.659871,123456790.12347022,21739130.434783,1879699.2481203708,104210.08753647323,41351.36252739525
+1.3,3816793893.129688,128205128.20513958,24390243.902435955,2770083.102492983,129550.45990413189,44871.21959974888
+1.35,3875968992.249229,135135135.13511705,27777777.77776912,3846153.846153427,167476.13465081353,49031.625398382064
+1.4,3921568627.450599,140845070.42259586,29411764.70589109,5050505.050505204,226398.00769753224,54024.8514316583
+1.45,3968253968.254057,144927536.23188666,31250000.000014223,6369426.751593516,320102.4327784888,60121.445319545724
+1.5,4032258064.5175858,151515151.51514998,34482758.620674506,7751937.984495421,469263.2566870025,67700.22341073764
+1.55,4065040650.405397,156249999.99992636,37037037.03702557,9174311.92660536,699300.6993006909,77345.50235903733
+1.6,4098360655.7361646,161290322.58059978,40000000.00002066,10526315.789476868,1035196.6873706528,89952.32526760765
+1.65,4149377593.359931,166666666.66677156,41666666.66666348,11764705.882350575,1490312.965722843,106928.99914456894
+1.7,4184100418.410862,172413793.10347325,41666666.66666357,13157894.73683673,2057613.168724073,130514.22605063971
+1.75,4201680672.270689,178571428.5713613,45454545.45455083,14492753.623192225,2732240.437158046,164365.54898093216
+1.8,4237288135.594598,178571428.5713613,47619047.61902008,15625000.000007112,3484320.557491879,214270.40925648183
+1.85,4291845493.561748,185185185.1851565,47619047.61905839,16949152.542364895,4255319.14893665,289100.89621277264
+1.9,4310344827.584264,188679245.28308737,52631578.94739372,18181818.18181189,5076142.131979034,401284.10914927017
+1.95,4329004329.003392,192307692.30770937,52631578.94734692,18867924.52830878,5917159.763314876,564971.7514124456
+2.0,4366812227.075336,199999999.9999,52631578.9473468,19999999.999996778,6711409.395973691,794281.1755361499
+2.05,4405286343.612994,200000000.00006983,58823529.41175301,21276595.74468023,7518796.992480046,1097694.84083421
+2.1,4424778761.061079,204081632.6530725,58823529.41181163,22727272.727275416,8264462.809917754,1472754.05007379
+2.15,4424778761.061079,208333333.3332811,58823529.41181163,23809523.809529245,9009009.009005764,1915708.8122608135
+2.2,4464285714.286986,212765957.446879,62499999.99999536,23809523.809529245,9803921.568625502,2409638.554216701
+2.25,4484304932.735116,217391304.34782895,62499999.99999508,24999999.99998745,10526315.7894806,2923976.608186506
+2.3,4484304932.735136,222222222.22215343,62499999.99999536,27027027.027017288,11235955.056180812,3460207.612456465
+2.35,4504504504.505309,222222222.22215343,66666666.66667113,27027027.027042035,12048192.771077603,4016064.2570291455
+2.4,4504504504.505309,222222222.22236255,66666666.66667113,27777777.77779532,12658227.848095236,4566210.045661982
+2.45,4484304932.735136,232558139.5348779,66666666.66667113,29411764.705876507,13157894.73684846,5128205.128202799
+2.5,4464285714.284329,232558139.53487688,71428571.42858742,30303030.303025976,13888888.8888976,5649717.5141227
+2.55,4464285714.286986,232558139.5348779,71428571.42850131,31249999.99999768,14492753.623185106,6134969.32515778
+2.6,4424778761.061079,238095238.0951484,76923076.92300856,31249999.99999768,15151515.151513057,6666666.666670125
+2.65,4347826086.956599,238095238.09538847,76923076.9231088,32258064.51612884,15873015.873015229,7194244.6043113
+2.7,4273504273.505232,238095238.0951484,76923076.9231088,33333333.333335564,16393442.62295131,7692307.692306871
+2.75,4184100418.4085264,238095238.0951473,76923076.92310846,34482758.620694496,16949152.54237459,8130081.300817547
+2.8,4065040650.4075847,238095238.09538847,76923076.9231088,35714285.71429387,17543859.649126068,8547008.547004664
+2.85,3906250000.000825,232558139.5348779,83333333.33326833,35714285.71429387,18181818.18182313,9009009.009005764
+2.9,3731343283.581178,227272727.2727979,83333333.33326833,37037037.03700233,18518518.518524405,9523809.523807088
+2.95,3546099290.779785,222222222.22215343,76923076.9231088,38461538.46150428,19230769.23075214,9900990.099008128
+3.0,3322259136.212644,212765957.44668636,76923076.92310846,38461538.46155423,19999999.99998318,10309278.35052883
+3.05,3086419753.087209,200000000.00006983,83333333.33338597,38461538.4615544,20000000.00001037,10869565.217399504
+3.1,2857142857.142861,188679245.2830878,83333333.33326833,40000000.00002075,20833333.333346494,11235955.056180812
+3.15,2610966057.440821,175438596.49120852,83333333.33326833,40000000.00002075,21739130.434766985,11494252.873564882
+3.2,2369668246.445191,156249999.99992636,83333333.33338597,39999999.99996654,21739130.434766985,11904761.90475502
+3.25,2136752136.7520025,140845070.42259553,76923076.92310846,41666666.66663398,22222222.222240344,12499999.999993725
+3.3,2024291497.975792,135135135.13527146,71428571.42858742,41666666.6666928,22727272.727292813,12820512.82050694
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
+,,,,,,
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_simulated_W10_L10.0.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_simulated_W10_L10.0.csv
new file mode 100644
index 0000000..c264eca
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/0_simulated_W10_L10.0.csv
@@ -0,0 +1,68 @@
+,-vds (V),vgs =-0.8,vgs =-1.3,vgs =-1.8,vgs =-2.3,vgs =-2.8,vgs =-3.3
+0,-0.0,,,,,,
+1,-0.05,852040.4247444011,56697.13691002142,29469.511948045252,20741.21786864382,16514.54502699289,14062.195527100223
+2,-0.1,1408866.1967256337,61123.87316458915,30579.121219416094,21245.872720994805,16806.88711410066,14255.321431304123
+3,-0.15,2061977.6229706989,66287.81530390415,31774.944188051948,21775.507050484557,17109.670834228884,14453.76852769568
+4,-0.2,2732852.6745590824,72378.40198664817,33067.35840582345,22332.01888335667,17423.465793054093,14657.760064428478
+5,-0.25,3407980.0098470994,79637.66544171513,34468.4333971418,22917.501111980073,17748.883037941985,14867.5318670232
+6,-0.3,4084462.424199627,88340.26287606372,35992.262959185246,23534.26702242947,18086.579515639227,15083.332773993076
+7,-0.35,4761410.158278116,98672.83291806448,37655.352549728326,24184.880559339625,18437.262284426524,15305.42645367606
+8,-0.4,5438477.965802579,110474.77945022171,39477.04170695176,24872.18835300336,18801.693169556864,15534.091764392706
+9,-0.45,6115510.147050133,123210.53434954863,41479.88504873029,25599.359073966738,19180.694830132696,15769.624430274134
+10,-0.5,6792429.144347457,136385.2080400324,43689.7630123078,26369.926377380627,19575.15577448949,16012.338159855566
+11,-0.55,7469193.673966147,149745.08784790136,46135.0953025246,27187.8371384491,19986.037971902337,16262.565807396551
+12,-0.6,8145780.858524166,163186.17570270767,48843.619573035285,28057.504603885787,20414.383402942978,16520.66110222899
+13,-0.65,8822177.770299647,176665.88989845806,51833.76827579922,28983.86191945492,20861.3230105345,16787.000231730333
+14,-0.7,9498377.134421032,190165.38468891173,55098.66301126393,29972.408813431342,21328.08511314832,17061.983563616
+15,-0.75,10174375.018496165,203675.54684175443,58592.643606096906,31029.235650673745,21816.005865957468,17346.037524960513
+16,-0.8,10850169.492735494,217191.60728842692,62243.50811880978,32160.984768067163,22326.53981658876,17639.616850764367
+17,-0.85,11525759.921555841,230710.90078986672,65985.2414769503,33374.667662912274,22861.2713799047,17943.20676989001
+18,-0.9,12201146.44086824,244231.85484658266,69775.31781707545,34677.171733109986,23421.926492698767,18257.325537855635
+19,-0.95,12876329.624198152,257753.50348736419,73590.90402792532,36074.14908439537,24010.383667000708,18582.52717440312
+20,-1.0,13551310.425676014,271275.2284165229,77420.40296111982,37567.89928196474,24628.6824063995,18919.40429879288
+21,-1.05,14226089.936283782,284796.62681016535,81257.84418729441,39154.31745709041,25279.0260373583,19268.591322817843
+22,-1.1,14900669.361096008,298317.4284130786,85100.0213817672,40820.694420649386,25963.771847490214,19630.767639771017
+23,-1.15,15575049.947187511,311837.4488021887,88945.12764700892,42547.61960109046,26685.39607101817,20006.661174345256
+24,-1.2,16249232.944803068,325356.5628664633,92792.0920104733,44315.00278325914,27446.410780027953,20397.05169388497
+25,-1.25,16923219.652990762,338874.68354831537,96640.25514264352,46107.1880062235,28249.189390075437,20802.773919760046
+26,-1.3,17597011.267295927,352391.75180697616,100489.19220958928,47914.0380288265,29095.633798628773,21224.72020108509
+27,-1.35,18270609.035526693,365907.72660919616,104338.62331956948,49729.58158635155,29986.60158658976,21663.841781934752
+28,-1.4,18944014.10560465,379422.58111105545,108188.35629924848,51550.40266240449,30921.08510162442,22121.14743409313
+29,-1.45,19617227.599801127,392936.2995843471,112038.2581753525,53374.53130581722,31895.414785340374,22597.69798746725
+30,-1.5,20290250.628449257,406448.87170714495,115888.23355552147,55200.80065890914,32903.167701580234,23094.592145361086
+31,-1.55,20963084.230473094,419960.2921853954,119738.21240644236,57028.495928939876,33936.3316628258,23612.93808390873
+32,-1.6,21635729.40364157,433470.5616243451,123588.14544053788,58857.16498323281,34987.18515622487,24153.800034255222
+33,-1.65,22308187.09912213,446979.6806856804,127437.9930749731,60686.51141580954,36049.63289861358,24718.102734430224
+34,-1.7,22980458.236149933,460487.6562112276,131287.72741090585,62516.33515063171,37119.47348411243,25306.471793734036
+35,-1.75,23652543.71119153,473994.4913118611,135137.32759478976,64346.498141496646,38194.02315830646,25918.98988903611
+36,-1.8,24324444.303002305,487500.1943907025,138986.77697563276,66176.90195458516,39271.61889911664,26554.88633144024
+37,-1.85,24996160.792373866,501004.77319704846,142836.06268084975,68007.47558757705,40351.23098660629,27212.266270572178
+38,-1.9,25667693.85535241,514508.2345323396,146685.17483587415,69838.16629617152,41432.21258798498,27888.093862611113
+39,-1.95,26339044.07707331,528010.5880257336,150534.10658972702,71668.93485981843,42514.14759095926,28578.597339843487
+40,-2.0,27010211.92909143,541511.8415494547,154382.8515789433,73499.75083216968,43596.76141632206,29279.964047951988
+41,-2.05,27681197.70390487,555012.0032716685,158231.40696449438,75330.59133165593,44679.868069634285,29988.93993266532
+42,-2.1,28352001.438661557,568511.0823885476,162079.76931819317,77161.43793205582,45763.33823486289,30703.07713198381
+43,-2.15,29022622.86724691,582009.0867048443,165927.934920193,78992.2763519878,46847.08026367567,31420.678633433312
+44,-2.2,29693061.20668498,595506.0213887574,169775.90529702447,80823.09534938619,47931.027356211205,32140.61789139928
+45,-2.25,30363315.16245461,609001.8924530807,173623.67605291435,82653.8859415901,49015.130166834366,32862.158432076256
+46,-2.3,31033382.39490708,622496.7022492909,177471.2487510557,84484.64073455677,50099.3513702325,33584.81766484894
+47,-2.35,31703259.56267239,635990.4529118596,181318.6220096441,86315.35340813217,51183.66248856971,34308.274990225975
+48,-2.4,32372941.771020293,649483.1379361057,185165.79706893343,88146.01937553077,52268.04130918403,35032.31369667784
+49,-2.45,33042422.094883416,662974.7452218498,189012.77386128312,89976.634830742,53352.47053786749,35756.783902920535
+50,-2.5,33711691.059115835,676465.2532323864,192859.55101170225,91807.19664642852,54436.936274768006,36481.5800649797
+51,-2.55,34380736.02975396,689954.6304504375,196706.1303905232,93637.70154386097,55521.427296255344,37206.626135927596
+52,-2.6,35049540.08492282,703442.8234151702,200552.5098228691,95468.14794653356,56605.934551373735,37931.86613137743
+53,-2.65,35718081.26586475,716929.7570269052,204398.6890994202,97298.53410505831,57690.45067103017,38657.25830884741
+54,-2.7,36386331.241257645,730415.3232153407,208244.66706039305,99128.85780051262,58774.969506348556,39382.7708688672
+55,-2.75,37054254.01545127,743899.3725425199,212090.4399601773,100959.11860207701,59859.48610352561,40108.379316593586
+56,-2.8,37721804.14098865,757381.703104652,215936.00131980082,102789.31508300234,60943.99611985248,40834.06434815613
+57,-2.85,38388924.95517164,770862.0443944645,219781.3417794352,104619.44632349536,62028.49596504525,41559.81080317563
+58,-2.9,39055546.4924242,784340.0356172055,223626.45239982955,106449.51122991137,63112.98280520516,42285.60631118915
+59,-2.95,39721582.88875861,797815.210900869,227471.3107785222,108279.5093632999,64197.45385411939,43011.440996648635
+60,-3.0,40386930.107147455,811286.9748703698,231315.89250056527,110109.43861521201,65281.907142501725,43737.30673196709
+61,-3.05,41051462.38512781,824754.5653450689,235160.16026049267,111939.297983226,66366.34076927419,44463.19680174505
+62,-3.1,41715029.52440423,838217.0200322836,239004.0648192285,113769.08519985127,67450.75309549131,45189.105510154724
+63,-3.15,42377452.92199964,851673.1502966426,242847.53864999526,115598.79666570207,68535.1426541371,45915.02819993897
+64,-3.2,43038521.84820771,865121.4762618872,246690.49095501224,117428.42781360421,69619.50819726415,46640.96091888151
+65,-3.25,43697989.1490955,878560.1928485578,250532.80812303527,119257.97220004197,70703.8487431215,47366.90031816113
+66,-3.3,44355566.8802337,891987.1103105295,254374.33671891698,121087.4200719828,71788.16315476834,48092.843391562164
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/diff_table.csv b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/diff_table.csv
new file mode 100644
index 0000000..b68d5f3
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/diff_table.csv
@@ -0,0 +1,80 @@
+,0,1,2,3,4,5
+0,100.0,,,,,
+1,99.999996,1294.909461,85.475612,38.740399,23.432232,15.866543
+2,99.999999,1847.174246,84.908885,36.961743,21.96087,14.680282
+3,100.0,2226.549972,84.225108,35.045737,20.417247,13.452003
+4,100.0,2297.804937,83.364891,32.975091,18.796291,12.1775
+5,100.0,2059.636932,82.207793,30.728427,17.090097,10.857749
+6,100.0,1569.483171,80.471206,28.287223,15.294374,9.490726
+7,100.0,973.412306,77.456183,25.618256,13.400486,8.068169
+8,100.0,474.248888,71.775025,22.68675,11.400044,6.590559
+9,100.0,178.378022,61.763111,19.453773,9.283664,5.056502
+10,100.0,40.603283,46.464075,15.86962,7.040805,3.458613
+11,100.0,19.855552,25.90567,11.863766,4.659579,1.794887
+12,100.0,48.111376,1.093836,7.343623,2.126511,0.0611
+13,100.0,62.593966,25.605994,2.179907,0.567963,1.747341
+14,100.0,70.839982,50.404868,3.825284,3.446882,3.637579
+15,100.0,75.784987,69.713446,10.880589,6.530633,5.610869
+16,100.0,78.950671,82.277165,19.164356,9.839895,7.675293
+17,100.0,81.097754,89.41037,28.749136,13.399412,9.839718
+18,100.0,82.552361,93.259201,39.490844,17.246397,12.104536
+19,100.0,83.647061,95.360437,51.010535,21.412466,14.484618
+20,100.0,84.415993,96.554805,62.598203,25.9424,16.989026
+21,100.0,84.920345,97.294432,73.241792,30.904376,19.620281
+22,100.0,85.397344,97.762619,81.958795,36.348291,22.39169
+23,100.0,85.670954,98.066608,88.259243,42.313937,25.318251
+24,100.0,86.02566,98.308146,92.353932,48.802877,28.411527
+25,100.0,86.292192,98.441176,94.858738,55.755542,31.684985
+26,100.0,86.274331,98.555194,96.37234,63.015154,35.157471
+27,100.0,86.479749,98.682732,97.287196,70.306467,38.842326
+28,100.0,86.54975,98.709963,97.857871,77.230187,42.765071
+29,100.0,86.464113,98.742604,98.240999,83.325796,46.948357
+30,100.0,86.608435,98.821298,98.505042,88.236709,51.398731
+31,100.0,86.583626,98.866107,98.694853,91.844925,56.123717
+32,100.0,86.585848,98.916324,98.825913,94.314398,61.104746
+33,100.0,86.615088,98.927249,98.916777,95.927935,66.286383
+34,100.0,86.671334,98.89483,99.002213,96.961706,71.559059
+35,100.0,86.754576,98.957212,99.067552,97.644918,76.762756
+36,100.0,86.378311,98.97625,99.110485,98.100723,81.671935
+37,100.0,86.502073,98.94789,99.157267,98.401824,86.042509
+38,100.0,86.396122,99.022434,99.193232,98.624188,89.675093
+39,100.0,86.303697,98.99678,99.202169,98.788795,92.474996
+40,100.0,86.494894,98.971128,99.228086,98.904854,94.511168
+41,100.0,86.159401,99.05648,99.256312,98.998103,95.929664
+42,100.0,86.107519,99.033531,99.286849,99.066347,96.892669
+43,100.0,86.069141,99.010585,99.303103,99.123186,97.554582
+44,100.0,86.044261,99.04719,99.286941,99.175604,98.010862
+45,100.0,86.032875,99.025597,99.305505,99.214788,98.323683
+46,100.0,86.034978,99.004005,99.343356,99.248087,98.552129
+47,100.0,85.733533,99.046014,99.329121,99.283583,98.725527
+48,100.0,85.432176,99.025775,99.333403,99.303646,98.85533
+49,100.0,85.791758,99.005538,99.357357,99.316178,98.959627
+50,100.0,85.503973,99.052949,99.363563,99.338988,99.036466
+51,100.0,85.216284,99.034064,99.37054,99.3539,99.095001
+52,100.0,85.279193,99.085524,99.358232,99.36991,99.150911
+53,100.0,84.998406,99.067991,99.366364,99.387019,99.198103
+54,100.0,84.717741,99.05046,99.375266,99.395314,99.235925
+55,100.0,84.437213,99.032931,99.384938,99.404341,99.263728
+56,100.0,84.156842,99.015404,99.395379,99.414101,99.286955
+57,100.0,83.492762,99.074966,99.384612,99.424593,99.311484
+58,100.0,82.81556,99.058792,99.396209,99.425173,99.337314
+59,100.0,82.125288,98.96284,99.408575,99.436947,99.351606
+60,100.0,81.018143,98.945327,99.398579,99.449453,99.366766
+61,100.0,79.474269,99.010295,99.388584,99.440304,99.38943
+62,100.0,77.891034,98.99414,99.40249,99.453908,99.399688
+63,100.0,75.844852,98.977992,99.392881,99.468246,99.403744
+64,100.0,72.455346,98.961854,99.383274,99.459829,99.415196
+65,100.0,68.974428,98.857872,99.398721,99.463339,99.434369
+66,100.0,67.176881,98.751218,99.389502,99.467215,99.440052
+67,,,,,,
+68,,,,,,
+69,,,,,,
+70,,,,,,
+71,,,,,,
+72,,,,,,
+73,,,,,,
+74,,,,,,
+75,,,,,,
+76,,,,,,
+77,,,,,,
+78,,,,,,
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/get_diff.py b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/get_diff.py
new file mode 100644
index 0000000..811fe74
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/get_diff.py
@@ -0,0 +1,14 @@
+import pandas as pd
+
+measured = pd.read_csv('0_measured_W10_L10.0.csv')
+simulated = pd.read_csv('0_simulated_W10_L10.0.csv')
+
+error_1 = round (100 * (abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1]),6)
+error_2 = round (100 * (abs(measured.iloc[:, 2]) - abs(simulated.iloc[:, 2]))/abs(measured.iloc[:, 2]),6)
+error_3 = round (100 * (abs(measured.iloc[:, 3]) - abs(simulated.iloc[:, 3]))/abs(measured.iloc[:, 3]),6)
+error_4 = round (100 * (abs(measured.iloc[:, 4]) - abs(simulated.iloc[:, 4]))/abs(measured.iloc[:, 4]),6)
+error_5 = round (100 * (abs(measured.iloc[:, 5]) - abs(simulated.iloc[:, 5]))/abs(measured.iloc[:, 5]),6)
+error_6 = round (100 * (abs(measured.iloc[:, 6]) - abs(simulated.iloc[:, 6]))/abs(measured.iloc[:, 6]),6)
+
+df = pd.DataFrame(data=[error_1,error_2,error_3,error_4,error_5,error_6]).transpose()
+df.to_csv("diff_table.csv")
\ No newline at end of file
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/rds_calc.py b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/rds_calc.py
new file mode 100644
index 0000000..dbf119a
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/0_man_error_debugging/pmos_3p3/Rds_large_signal/rds_calc.py
@@ -0,0 +1,14 @@
+import pandas as pd
+
+
+df = pd.read_csv ("../../../pmos_3p3_iv/simulated_Id/0_simulated_W10_L10.0.csv")
+
+df["vgs =-0.8"] = df["-vds (V)"]/df["vgs =-0.8"]
+df["vgs =-1.3"] = df["-vds (V)"]/df["vgs =-1.3"]
+df["vgs =-1.8"] = df["-vds (V)"]/df["vgs =-1.8"]
+df["vgs =-2.3"] = df["-vds (V)"]/df["vgs =-2.3"]
+df["vgs =-2.8"] = df["-vds (V)"]/df["vgs =-2.8"]
+df["vgs =-3.3"] = df["-vds (V)"]/df["vgs =-3.3"]
+
+df.to_csv ("0_simulated_W10_L10.0.csv")
+
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice
new file mode 100644
index 0000000..524d1f9
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice
@@ -0,0 +1,48 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3 W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+print -i(Vds)
+wrdata nmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..a22503c
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice
@@ -0,0 +1,47 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 nmos_3p3_sab W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+print -i(Vds)
+wrdata nmos_3p3_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice
new file mode 100644
index 0000000..c1842a2
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice
@@ -0,0 +1,46 @@
+***************************
+** nmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc 6.6
+Vgs G_tn 0 6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 nmos_6p0 W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 6.6 0.05 Vgs 1 6 1
+print -i(Vds)
+wrdata nmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice
new file mode 100644
index 0000000..5a93ceb
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice
@@ -0,0 +1,46 @@
+***************************
+** nmos_6p0_nat_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc 6.6
+Vgs G_tn 0 6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_nat W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 6.6 0.05 Vgs 0.25 6 1.15
+print -i(Vds)
+wrdata nmos_6p0_nat_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..7bcdab8
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice
@@ -0,0 +1,47 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc 6.6
+Vgs G_tn 0 6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_sab W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 6.6 0.05 Vgs 1 6 1
+print -i(Vds)
+wrdata nmos_6p0_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice
new file mode 100644
index 0000000..90099fe
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice
@@ -0,0 +1,46 @@
+***************************
+** pmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 pmos_3p3 W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds -0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+print -i(Vds)
+wrdata pmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..aaa16ab
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice
@@ -0,0 +1,47 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 -3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 pmos_3p3_sab W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+print -i(Vds)
+wrdata pmos_3p3_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice
new file mode 100644
index 0000000..d4d2cca
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice
@@ -0,0 +1,47 @@
+***************************
+** pmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc -6.6
+Vgs G_tn 0 -6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 pmos_6p0 W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+print -i(Vds)
+wrdata pmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..10ef8b1
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice
@@ -0,0 +1,47 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vds D_tn 0 dc -6.6
+Vgs G_tn 0 -6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+xmn1 D_tn G_tn 0 0 pmos_6p0_sab W = {{width}}u L = {{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+**** begin architecture code
+
+
+.control
+set filetype=ascii
+
+dc Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+print -i(Vds)
+wrdata pmos_6p0_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv -i(Vds)
+.endc
+
+
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+**** end architecture code
+
+
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice
new file mode 100644
index 0000000..aeaf953
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+mn D_tn G_tn 0 0 nmos_3p3 W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.05
+let vds_max = 3.3
+
+compose vgs_vector start=0.8 stop=3.3 step=0.5
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[vth] @mn[id] @mn[gm] @mn[gmbs] @mn[gds] @mn[cgg] @mn[cgs] @mn[cgd] @mn[cdd] @mn[cdb] @mn[cgb] @mn[csb]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@mn[gds]
+ print Rds
+ wrdata nmos_3p3_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..dfefba2
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+xmn1 D_tn G_tn 0 0 nmos_3p3_sab W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.05
+let vds_max = 3.3
+
+compose vgs_vector start=0.8 stop=3.3 step=0.5
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save all @m.xmn1.m0[gds]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@m.xmn1.m0[gds]
+ print Rds
+ wrdata nmos_3p3_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice
new file mode 100644
index 0000000..ba3d9a9
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=6.6
+vgs G_tn 0 dc=6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+mn D_tn G_tn 0 0 nmos_6p0 W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.05
+let vds_max = 6.6
+
+compose vgs_vector start=1 stop=6 step=1
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[vth] @mn[id] @mn[gm] @mn[gmbs] @mn[gds] @mn[cgg] @mn[cgs] @mn[cgd] @mn[cdd] @mn[cdb] @mn[cgb] @mn[csb]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@mn[gds]
+ print Rds
+ wrdata nmos_6p0_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[gds]
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice
new file mode 100644
index 0000000..2ea4dc8
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_6p0_nat_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=6.6
+vgs G_tn 0 dc=6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+mn D_tn G_tn 0 0 nmos_6p0_nat W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.05
+let vds_max = 6.6
+
+compose vgs_vector start=0.25 stop=6 step=1.15
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[vth] @mn[id] @mn[gm] @mn[gmbs] @mn[gds] @mn[cgg] @mn[cgs] @mn[cgd] @mn[cdd] @mn[cdb] @mn[cgb] @mn[csb]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@mn[gds]
+ print Rds
+ wrdata nmos_6p0_nat_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..e7d46fc
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=6.6
+vgs G_tn 0 dc=6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+xmn1 D_tn G_tn 0 0 nmos_6p0_sab W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.05
+let vds_max = 6.6
+
+compose vgs_vector start=1 stop=6 step=1
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save all @m.xmn1.m0[gds]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = @m.xmn1.m0[gds]
+ print Rds
+ wrdata nmos_6p0_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice
new file mode 100644
index 0000000..56e3016
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** pmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+mn D_tn G_tn 0 0 pmos_3p3 W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.05
+let vds_max = -3.3
+
+compose vgs_vector start=-0.8 stop=-3.3 step=-0.5
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[vth] @mn[id] @mn[gm] @mn[gmbs] @mn[gds] @mn[cgg] @mn[cgs] @mn[cgd] @mn[cdd] @mn[cdb] @mn[cgb] @mn[csb]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@mn[gds]
+ print Rds
+ wrdata pmos_3p3_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..0d50993
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+xmn1 D_tn G_tn 0 0 pmos_3p3_sab W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.05
+let vds_max = -3.3
+
+compose vgs_vector start=-0.8 stop=-3.3 step=-0.5
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save all @m.xmn1.m0[gds]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@m.xmn1.m0[gds]
+ print Rds
+ wrdata pmos_3p3_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice
new file mode 100644
index 0000000..de3e491
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** pmos_6p0_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-6.6
+vgs G_tn 0 dc=-6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+mn D_tn G_tn 0 0 pmos_6p0 W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.05
+let vds_max = -6.6
+
+compose vgs_vector start=-1 stop=-6 step=-1
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[vth] @mn[id] @mn[gm] @mn[gmbs] @mn[gds] @mn[cgg] @mn[cgs] @mn[cgd] @mn[cdd] @mn[cdb] @mn[cgb] @mn[csb]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = 1/@mn[gds]
+ print Rds
+ wrdata pmos_6p0_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[gds]
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..780a169
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice
@@ -0,0 +1,68 @@
+***************************
+** nmos_3p3_t_id
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-6.6
+vgs G_tn 0 dc=-6
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+* circuit
+xmn1 D_tn G_tn 0 0 pmos_6p0_sab W = {{width}}u L = {{length}}u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.05
+let vds_max = -6.6
+
+compose vgs_vector start=-1 stop=-6 step=-1
+
+set appendwrite
+
+foreach t {{temp}}
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=$t
+ alter vgs = vgs_vector[vgs_counter]
+
+ save all @m.xmn1.m0[gds]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+ let Rds = @m.xmn1.m0[gds]
+ print Rds
+ wrdata pmos_6p0_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv Rds
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/ngspice/testing/regression/mos_iv_vgs/models_regression.py b/models/ngspice/testing/regression/mos_iv_vgs/models_regression.py
new file mode 100644
index 0000000..76815a2
--- /dev/null
+++ b/models/ngspice/testing/regression/mos_iv_vgs/models_regression.py
@@ -0,0 +1,294 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vds,vgs):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops*2,2):
+ width = dimensions["W (um)"].iloc[int(i/2)]
+ length = dimensions["L (um)"].iloc[int(i/2)]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ # measured Rds
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i+1}",f"vgs ={vgs[1]}.{i+1}",f"vgs ={vgs[2]}.{i+1}",f"vgs ={vgs[3]}.{i+1}",f"vgs ={vgs[4]}.{i+1}",f"vgs ={vgs[5]}.{i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Rds/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i}",f"vgs ={vgs[1]}.{i}",f"vgs ={vgs[2]}.{i}",f"vgs ={vgs[3]}.{i}",f"vgs ={vgs[4]}.{i}",f"vgs ={vgs[5]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ # measured Rds
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i+1}",f"vgs ={vgs[1]}.{i+1}",f"vgs ={vgs[2]}.{i+1}",f"vgs ={vgs[3]}.{i+1}",f"vgs ={vgs[4]}.{i+1}",f"vgs ={vgs[5]}.{i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Rds/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+
+def ext_simulated(device,vds,vgs,vds_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ AD = float(width) * 0.24
+ PD = 2 * (float(width) + 0.24)
+ AS = AD
+ PS = PD
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else:
+ temp = 125
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i , temp = temp , AD = AD , PD = PD , AS = AS , PS = PD ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+ # empty array to append in it shaped (vds_sweep, number of trials + 1)
+ new_array = np.empty((vds_sweep, 1+int(df_simulated.shape[0]/vds_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vds_sweep, 0]
+ times = int(df_simulated.shape[0]/vds_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vds_sweep:(j+1)*vds_sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ df_simulated.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,vds,vgs,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else: temp = 125
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[1:, 1]) - abs(simulated.iloc[1:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[1:, 2]) - abs(simulated.iloc[1:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[1:, 3]) - abs(simulated.iloc[1:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[1:, 4]) - abs(simulated.iloc[1:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[1:, 5]) - abs(simulated.iloc[1:, 5]))/abs(measured.iloc[:, 5])),6)
+ error_6 = round (100 * abs((abs(measured.iloc[1:, 6]) - abs(simulated.iloc[1:, 6]))/abs(measured.iloc[:, 6])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5,error_6]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"vgs ={vgs[0]}"].mean() + df_error[f"vgs ={vgs[1]}"].mean() + df_error[f"vgs ={vgs[2]}"].mean() +
+ df_error[f"vgs ={vgs[3]}"].mean() + df_error[f"vgs ={vgs[4]}"].mean() + df_error[f"vgs ={vgs[5]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}" ]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vds}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & Vds (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_iv" , "pmos_3p3_iv" , "nmos_6p0_iv" , "pmos_6p0_iv" , "nmos_6p0_nat_iv", "nmos_3p3_sab_iv", "pmos_3p3_sab_iv" , "nmos_6p0_sab_iv" , "pmos_6p0_sab_iv"]
+ nmos_vds = "vds (V)"
+ pmos_vds = "-vds (V)"
+ nmos_rds = "Rds"
+ Id_sim = "Id"
+ Rds_sim = "Rds"
+ mos_3p3_vgs_sweep = 67
+ mos_6p0_vgs_sweep = 133
+ nmos3p3_vgs = [ 0.8 , 1.3 , 1.8 , 2.3 , 2.8 , 3.3]
+ pmos3p3_vgs = [-0.8 , -1.3 , -1.8 , -2.3 , -2.8 , -3.3]
+ nmos6p0_vgs = [ 1 , 2 , 3 , 4 , 5 , 6]
+ pmos6p0_vgs = [-1 , -2 , -3 , -4 , -5 , -6]
+ nmos6p0_nat_vgs = [ 0.25 , 1.4 , 2.55 , 3.7 , 4.85 , 6]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{Rds_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/MOS/{device}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{Rds_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Rds_sim}",exist_ok=False)
+
+ # =========== nmos_3p3_iv ==============
+ ext_measured ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs)
+
+ ext_simulated("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+ error_cal ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,Id_sim)
+
+ ext_simulated("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+ error_cal ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,Rds_sim)
+
+ # =========== pmos_3p3_iv ==============
+ ext_measured ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs)
+
+ ext_simulated("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+ error_cal ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,Id_sim)
+
+ ext_simulated("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+ error_cal ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,Rds_sim)
+
+ # =========== nmos_6p0_iv ==============
+ ext_measured ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs)
+
+ ext_simulated("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+ error_cal ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,Id_sim)
+
+ ext_simulated("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+ error_cal ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,Rds_sim)
+
+ # =========== pmos_6p0_iv ==============
+ ext_measured ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs)
+
+ ext_simulated("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+ error_cal ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,Id_sim)
+
+ ext_simulated("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+ error_cal ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,Rds_sim)
+
+ # ============ nmos_6p0_nat_iv =============
+ ext_measured ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs)
+
+ ext_simulated("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,mos_6p0_vgs_sweep,Id_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,Id_sim)
+
+ ext_simulated("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,mos_6p0_vgs_sweep,Rds_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,Rds_sim)
+
+ # ============ nmos_3p3_sab_iv =============
+ ext_measured ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs)
+
+ ext_simulated("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+ error_cal ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,Id_sim)
+
+ ext_simulated("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+ error_cal ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,Rds_sim)
+
+ # =========== pmos_3p3_sab_iv ==============
+ ext_measured ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs)
+
+ ext_simulated("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+ error_cal ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,Id_sim)
+
+ ext_simulated("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+ error_cal ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,Rds_sim)
+
+ # =========== nmos_6p0_sab_iv ==============
+ ext_measured ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs)
+
+ ext_simulated("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+ error_cal ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,Id_sim)
+
+ ext_simulated("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+ error_cal ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,Rds_sim)
+
+ # =========== pmos_6p0_sab_iv ==============
+ ext_measured ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs)
+
+ ext_simulated("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+ error_cal ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,Id_sim)
+
+ ext_simulated("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+ error_cal ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,Rds_sim)
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/moscap_c/.spiceinit b/models/ngspice/testing/regression/moscap_c/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/moscap_c/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/moscap_c/device_netlists/.spiceinit b/models/ngspice/testing/regression/moscap_c/device_netlists/.spiceinit
new file mode 100644
index 0000000..84222e2
--- /dev/null
+++ b/models/ngspice/testing/regression/moscap_c/device_netlists/.spiceinit
@@ -0,0 +1,5 @@
+
+* user provided init file
+set ngbehavior=hs
+
+
diff --git a/models/ngspice/testing/regression/moscap_c/device_netlists/moscap.spice b/models/ngspice/testing/regression/moscap_c/device_netlists/moscap.spice
new file mode 100644
index 0000000..6150abb
--- /dev/null
+++ b/models/ngspice/testing/regression/moscap_c/device_netlists/moscap.spice
@@ -0,0 +1,38 @@
+***************************
+** cap
+***************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+.options noacct
+
+Ich 0 p dc 10u
+
+.temp 25
+.options tnom=25
+
+xcn p 0 {{device}} c_length={{length}}u c_width={{width}}u
+
+.ic v(p)=0.0
+.tran 10ns 100us
+
+* .print tran v(p) cj=par('10.0e-6 * time / v(p)')
+
+.meas tran CV FIND par('(10.0e-6 * time / v(p))*1.0e15') AT=100us
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" moscap_{{corner}}
+.end
+
diff --git a/models/ngspice/testing/regression/moscap_c/models_regression.py b/models/ngspice/testing/regression/moscap_c/models_regression.py
new file mode 100644
index 0000000..dc0c019
--- /dev/null
+++ b/models/ngspice/testing/regression/moscap_c/models_regression.py
@@ -0,0 +1,191 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ # measured cv
+ col_list = [f"{vn}",f"{d_in}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ netlist_tmp = f"./device_netlists/moscap.spice"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice.log")
+ clean_data = str(df_simulated.loc[4]).replace("Compatibility modes selected: hs","").replace("\nName: 4, dtype: object","").split("=")
+ df_clean_ = {vn: [f"moscap_{corner}"],d_in: [clean_data[1]]}
+ df_clean = pd.DataFrame(df_clean_)
+ df_clean.to_csv(f"{dirpath}/simulated_{cv_sim}/{i-start}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ df_final = pd.DataFrame()
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i-start}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i-start}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i-start}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}"].mean())
+ # Max error
+ max_error = df_error[f"{d_in}"].max()
+
+ df_final_ = {'Run no.': f'{i-start}', 'Device name': f'{dirpath}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+
+def main():
+
+ # 3p3
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_3p3" , "pmoscap_3p3" , "nmoscap_3p3_b" , "pmoscap_3p3_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start)
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = start + 32
+
+ # 6p0
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_6p0" , "pmoscap_6p0" , "nmoscap_6p0_b" , "pmoscap_6p0_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start)
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = start + 32
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/regression/resistor_r/.spiceinit b/models/ngspice/testing/regression/resistor_r/.spiceinit
new file mode 100644
index 0000000..7779a4c
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/.spiceinit
@@ -0,0 +1,2 @@
+* user provided init file
+set ngbehavior=hs
diff --git a/models/ngspice/testing/regression/resistor_r/device_netlists/.spiceinit b/models/ngspice/testing/regression/resistor_r/device_netlists/.spiceinit
new file mode 100644
index 0000000..84222e2
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/device_netlists/.spiceinit
@@ -0,0 +1,5 @@
+
+* user provided init file
+set ngbehavior=hs
+
+
diff --git a/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_a.spice b/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_a.spice
new file mode 100644
index 0000000..1f39ae3
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_a.spice
@@ -0,0 +1,44 @@
+**************************
+** res
+**************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+Vn n 0 DC {{sign}}3.3
+
+.temp 25
+.options tnom=25
+
+r1 n p 1M
+xrn p 0 {{device}} r_width={{width}}u r_length={{length}}u
+
+
+.print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/{{length}}u*{{width}}u')
+
+
+.control
+set filetype=ascii
+set appendwrite
+
+DC Vn {{sign}}3.3 {{sign}}3.3 0.1
+wrdata {{device}}_r_{{corner}}/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv Rs
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" res_{{corner}}
+
+.end
diff --git a/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_b.spice b/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_b.spice
new file mode 100644
index 0000000..21db7ab
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/device_netlists/2term_res_b.spice
@@ -0,0 +1,44 @@
+**************************
+** res
+**************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+Vn n 0 DC {{sign}}3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+r1 n p 1M
+xrn p 0 {{device}} r_width={{width}}u r_length={{length}}u
+
+
+.print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/{{length}}u*{{width}}u')
+
+
+.control
+set filetype=ascii
+set appendwrite
+
+DC Vn {{sign}}3.3 {{sign}}3.3 0.1
+wrdata {{device}}_r_{{corner}}_temp/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv Rs
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" res_{{corner}}
+
+.end
diff --git a/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_a.spice b/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_a.spice
new file mode 100644
index 0000000..8653599
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_a.spice
@@ -0,0 +1,44 @@
+**************************
+** res
+**************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+Vn n 0 DC {{sign}}3.3
+
+.temp 25
+.options tnom=25
+
+r1 n p 1M
+xrn p 0 0 {{device}} r_width={{width}}u r_length={{length}}u
+
+
+.print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/{{length}}u*{{width}}u')
+
+
+.control
+set filetype=ascii
+set appendwrite
+
+DC Vn {{sign}}3.3 {{sign}}3.3 0.1
+wrdata {{device}}_r_{{corner}}/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv Rs
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" res_{{corner}}
+
+.end
diff --git a/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_b.spice b/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_b.spice
new file mode 100644
index 0000000..120aa9d
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/device_netlists/3term_res_b.spice
@@ -0,0 +1,44 @@
+**************************
+** res
+**************************
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+Vn n 0 DC {{sign}}3.3
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+r1 n p 1M
+xrn p 0 0 {{device}} r_width={{width}}u r_length={{length}}u
+
+
+.print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/{{length}}u*{{width}}u')
+
+
+.control
+set filetype=ascii
+set appendwrite
+
+DC Vn {{sign}}3.3 {{sign}}3.3 0.1
+wrdata {{device}}_r_{{corner}}_temp/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv Rs
+
+.endc
+
+** library calling
+
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" res_{{corner}}
+
+.end
diff --git a/models/ngspice/testing/regression/resistor_r/models_regression.py b/models/ngspice/testing/regression/resistor_r/models_regression.py
new file mode 100644
index 0000000..f166171
--- /dev/null
+++ b/models/ngspice/testing/regression/resistor_r/models_regression.py
@@ -0,0 +1,323 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured_a(device,vn,d_in, r_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ # measured r
+ col_list = [f"{vn}",f"{d_in}_{corner} Rev9 "]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}","value"]
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_measured_b(device,vn,d_in, r_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ # measured r
+ col_list = [f"{vn}",f"{d_in}_{corner} Rev9 "]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}","value"]
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated_a(device,vn,d_in,r_sim, corner,sign):
+
+ if "rm" in device or "tm" in device:
+ netlist_tmp = f"./device_netlists/2term_res_a.spice"
+ else:
+ netlist_tmp = f"./device_netlists/3term_res_a.spice"
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+ temp = 25
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{r_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner, i = i , sign = sign))
+ netlist_path = f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+ # Writing final simulated data
+ df_simulated.columns = [f"{vn}","value"]
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+def ext_simulated_b(device,vn,d_in,r_sim, corner,sign):
+
+ if "rm" in device or "tm" in device:
+ netlist_tmp = f"./device_netlists/2term_res_b.spice"
+ else:
+ netlist_tmp = f"./device_netlists/3term_res_b.spice"
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{r_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, temp = temp , width = width, length = length , corner = corner, i = i , sign = sign))
+ netlist_path = f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+ # Writing final simulated data
+ df_simulated.columns = [f"{vn}","value"]
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal_a(device,vn,d_in,r_sim, corner):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+ temp = 25
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ measured = pd.read_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{r_sim}/{i}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{r_sim}/{i}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"value"].mean())
+ # Max error
+ max_error = df_error[f"value"].max()
+
+ df_final_ = {'Run no.': f'{i}', 'Device name': f'{dirpath}', 'Temperature': temp, 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{r_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{r_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{r_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def error_cal_b(device,vn,d_in,r_sim, corner):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ measured = pd.read_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{r_sim}/{i}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{r_sim}/{i}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"value"].mean())
+ # Max error
+ max_error = df_error[f"value"].max()
+
+ df_final_ = {'Run no.': f'{i}', 'Device name': f'{dirpath}', 'Temperature': temp, 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{r_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{r_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{r_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ # res W&L var.
+ corners_a = ["typical","ff","ss"]
+
+ devices_a = ["nplus_u" , "pplus_u" , "nplus_s" , "pplus_s" , "npolyf_u" , "ppolyf_u" , "npolyf_s" , "ppolyf_s" , "ppolyf_u_1k" , "ppolyf_u_2k" , "ppolyf_u_1k_6p0" ,
+ "ppolyf_u_2k_6p0" , "ppolyf_u_3k" , "rm1" , "rm2" , "rm3" , "tm6k" , "tm9k" , "tm11k" , "tm30k" , "nwell"]
+
+ dev_data_a = ["RES01a-wl-nplus_u.nl_out" ,"RES02a-wl-pplus_u.nl_out" ,"RES03a-wl-nplus_s.nl_out" , "RES04a-wl-pplus_s.nl_out" ,
+ "RES06a-wl-npolyf_u.nl_out" ,"RES07a-wl-ppolyf_u.nl_out" ,"RES08a-wl-npolyf_s.nl_out" , "RES09a-wl-ppolyf_s.nl_out" , "RES10a-wl-ppolyf_u_1k.nl_out",
+ "RES11a-wl-ppolyf_u_2k.nl_out" ,"RES12a-wl-ppolyf_u_1k_6p0.nl_o" ,"RES13a-wl-ppolyf_u_2k_6p0.nl_o" , "RES14a-wl-ppolyf_u_3k.nl_out" ,
+ "RES15a-wl-rm1.nl_out" , "RES16a-wl-rm2.nl_out" , "RES17a-wl-rm3.nl_out" , "RES18a-wl-tm6k.nl_out" , "RES19a-wl-tm9k.nl_out" ,
+ "RES20a-wl-tm11k.nl_out" , "RES21a-wl-tm30k.nl_out" ,"RES05a-wl-nwell.nl_out"]
+
+ sign_a = ["+" , "-" , "+" , "-" , "+" , "+" , "-" , "+" , "-" , "-" , "-" , "-" , "-" , "-" , "+" , "+" , "+" , "+" , "+" , "+", "+", ""]
+ measure_a = ["r","corners", "res"]
+
+ for corner in corners_a:
+ for i,device in enumerate(devices_a):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_a[0], measure_a[1], measure_a[2]
+ dirpath = f"{device}_{r_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{r_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Resistor/{dev_data_a[i]}.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{r_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{r_sim}",exist_ok=False)
+
+ ext_measured_a (device,res_vn,res_in, r_sim, corner)
+ ext_simulated_a(device,res_vn,res_in,r_sim, corner,sign_a[i])
+ error_cal_a (device,res_vn,res_in,r_sim, corner)
+
+
+ # res temp_var
+ corners_b = ["typical","ff","ss"]
+ devices_b = ["nplus_u" , "nplus_s", "pplus_u" , "pplus_s" , "nwell" , "npolyf_u" , "ppolyf_u" , "npolyf_s" , "ppolyf_s" , "ppolyf_u_1k" , "ppolyf_u_2k" , "ppolyf_u_1k_6p0" , "ppolyf_u_2k_6p0" , "ppolyf_u_3k" ,
+ "rm1" , "rm2", "rm3" , "tm6k" , "tm9k" , "tm11k", "tm30k"]
+ dev_data_b = ["RES01b-temp-nplus_u.nl_out" , "RES03b-temp-nplus_s.nl_out", "RES02b-temp-pplus_u.nl_out" , "RES04b-temp-pplus_s.nl_out" , "RES05b-temp-nwell.nl_out" ,
+ "RES06b-temp-npolyf_u.nl_out" , "RES07b-temp-ppolyf_u.nl_out" , "RES08b-temp-npolyf_s.nl_out" , "RES09b-temp-ppolyf_s.nl_out" , "RES10b-temp-ppolyf_u_1k.nl_out" ,
+ "RES11b-temp-ppolyf_u_2k.nl_out" , "RES12b-temp-ppolyf_u_1k_6p0.nl" , "RES13b-temp-ppolyf_u_2k_6p0.nl" , "RES14b-temp-ppolyf_u_3k.nl_out" ,
+ "RES15b-temp-rm1.nl_out", "RES16b-temp-rm2.nl_out" , "RES17b-temp-rm3.nl_out" ,
+ "RES18b-temp-tm6k.nl_out", "RES19b-temp-tm9k.nl_out" , "RES20b-temp-tm11k.nl_out", "RES21b-temp-tm30k.nl_out"]
+
+ sign_b = ["+" , "+", "-" , "-" , "+" , "+" , "-" ,"+" , "-" , "-" , "-" , "-" , "-" , "-" , "+" , "+" , "+" , "+" , "+" , "+", "+"]
+ measure_b = ["r","corners", "res"]
+
+ for corner in corners_b:
+ for i,device in enumerate(devices_b):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_b[0], measure_b[1], measure_b[2]
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{r_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Resistor/{dev_data_b[i]}.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{r_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{r_sim}",exist_ok=False)
+
+ ext_measured_b (device,res_vn,res_in, r_sim, corner)
+ ext_simulated_b(device,res_vn,res_in,r_sim, corner,sign_b[i])
+ error_cal_b (device,res_vn,res_in,r_sim, corner)
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/.spiceinit b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/.spiceinit b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice
new file mode 100644
index 0000000..9a49154
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice
@@ -0,0 +1,46 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vin I 0 dc pulse(0 5 100p 1p 1p 100p 200p)
+Vdd VDD 0 dc {{volt}}
+
+* Main circuit
+X1 I ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__inv_1
+
+* Temperature set
+.temp {{temp}}
+.options tnom={{temp}}
+
+* Analyses
+.control
+tran 1p 200p
+meas tran high_in FIND V(ZN) AT=100p
+meas tran low_in FIND V(ZN) AT=200p
+
+wrdata inv/simulated/inv_{{process}}_{{temp}}c_{{volt}}v.csv {high_in} {low_in}
+
+.endc
+
+* Libraries calling
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__inv_1 I ZN VDD VNW VPW VSS
+xM_i_0 ZN I VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_1 ZN I VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice
new file mode 100644
index 0000000..76b31f6
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice
@@ -0,0 +1,46 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vin I 0 dc pulse(0 5 100p 1p 1p 100p 200p)
+Vdd VDD 0 dc 5
+
+* Main circuit
+X1 I ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__inv_1
+
+* Temperature set
+.temp 25
+.options tnom=25
+
+* Analyses
+.control
+tran 1p 200p
+meas tran high_in FIND V(ZN) AT=100p
+meas tran low_in FIND V(ZN) AT=200p
+
+wrdata inv/inv_simulated.csv {high_in} {low_in}
+
+.endc
+
+* Libraries calling
+.include "../../../../design.ngspice"
+.lib "../../../../sm141064.ngspice" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__inv_1 I ZN VDD VNW VPW VSS
+xM_i_0 ZN I VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_1 ZN I VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice
new file mode 100644
index 0000000..56e4523
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice
@@ -0,0 +1,50 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc {{volt}}
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+
+* Main circuit
+X1 I1 I2 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__nand2_1
+
+* Temperature set
+.temp {{temp}}
+.options tnom={{temp}}
+
+* Analyses
+.control
+tran 1p 800p
+meas tran o0 FIND V(ZN) AT=200p
+meas tran o1 FIND V(ZN) AT=400p
+meas tran o2 FIND V(ZN) AT=600p
+meas tran o3 FIND V(ZN) AT=800p
+
+wrdata nand2/simulated/nand2_{{process}}_{{temp}}c_{{volt}}v.csv {o0} {o1} {o2} {o3}
+.endc
+
+* Libraries calling
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__nand2_1 A1 A2 ZN VDD VNW VPW VSS
+xM_i_1 net_0 A2 VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_0 ZN A1 net_0 VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_3 ZN A2 VDD VNW pmos_6p0 W=1.13e-06 L=5e-07
+xM_i_2 VDD A1 ZN VNW pmos_6p0 W=1.13e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice
new file mode 100644
index 0000000..ff981f9
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice
@@ -0,0 +1,50 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc 5
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+
+* Main circuit
+X1 I1 I2 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__nand2_1
+
+* Temperature set
+.temp 25
+.options tnom=25
+
+* Analyses
+.control
+tran 1p 800p
+meas tran o0 FIND V(ZN) AT=200p
+meas tran o1 FIND V(ZN) AT=400p
+meas tran o2 FIND V(ZN) AT=600p
+meas tran o3 FIND V(ZN) AT=800p
+
+wrdata nand2/nand2_simulated.csv {o0} {o1} {o2} {o3}
+.endc
+
+* Libraries calling
+.include "../../../../design.ngspice"
+.lib "../../../../sm141064.ngspice" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__nand2_1 A1 A2 ZN VDD VNW VPW VSS
+xM_i_1 net_0 A2 VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_0 ZN A1 net_0 VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_3 ZN A2 VDD VNW pmos_6p0 W=1.13e-06 L=5e-07
+xM_i_2 VDD A1 ZN VNW pmos_6p0 W=1.13e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice
new file mode 100644
index 0000000..f269c03
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice
@@ -0,0 +1,59 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc {{volt}}
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+Vin3 I3 0 dc pulse(0 5 800p 1p 1p 800p 1600p)
+
+* Main circuit
+X1 I1 I2 I3 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__or3_1
+
+* Temperature set
+.temp {{temp}}
+.options tnom={{temp}}
+
+* Analyses
+.control
+tran 1p 1600p
+meas tran o0 FIND V(ZN) AT=200p
+meas tran o1 FIND V(ZN) AT=400p
+meas tran o2 FIND V(ZN) AT=600p
+meas tran o3 FIND V(ZN) AT=800p
+meas tran o4 FIND V(ZN) AT=1000p
+meas tran o5 FIND V(ZN) AT=1200p
+meas tran o6 FIND V(ZN) AT=1400p
+meas tran o7 FIND V(ZN) AT=1600p
+
+wrdata or3/simulated/or3_{{process}}_{{temp}}c_{{volt}}v.csv {o0} {o1} {o2} {o3} {o4} {o5} {o6} {o7}
+.endc
+
+* Libraries calling
+.include "../../../design.ngspice"
+.lib "../../../sm141064.ngspice" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__or3_1 A1 A2 A3 Z VDD VNW VPW VSS
+M_i_2 VSS A1 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_3 Z_neg A2 VSS VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_4 VSS A3 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_0 Z Z_neg VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+M_i_5 net_0 A1 Z_neg VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_6 net_1 A2 net_0 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_7 VDD A3 net_1 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_1 Z Z_neg VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice
new file mode 100644
index 0000000..24a5c05
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice
@@ -0,0 +1,59 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc 5
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+Vin3 I3 0 dc pulse(0 5 800p 1p 1p 800p 1600p)
+
+* Main circuit
+X1 I1 I2 I3 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__or3_1
+
+* Temperature set
+.temp 25
+.options tnom=25
+
+* Analyses
+.control
+tran 1p 1600p
+meas tran o0 FIND V(ZN) AT=200p
+meas tran o1 FIND V(ZN) AT=400p
+meas tran o2 FIND V(ZN) AT=600p
+meas tran o3 FIND V(ZN) AT=800p
+meas tran o4 FIND V(ZN) AT=1000p
+meas tran o5 FIND V(ZN) AT=1200p
+meas tran o6 FIND V(ZN) AT=1400p
+meas tran o7 FIND V(ZN) AT=1600p
+
+wrdata or3/or3_simulated.csv {o0} {o1} {o2} {o3} {o4} {o5} {o6} {o7}
+.endc
+
+* Libraries calling
+.include "../../../../design.ngspice"
+.lib "../../../../sm141064.ngspice" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__or3_1 A1 A2 A3 Z VDD VNW VPW VSS
+M_i_2 VSS A1 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_3 Z_neg A2 VSS VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_4 VSS A3 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_0 Z Z_neg VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+M_i_5 net_0 A1 Z_neg VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_6 net_1 A2 net_0 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_7 VDD A3 net_1 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_1 Z Z_neg VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py
new file mode 100644
index 0000000..154994b
--- /dev/null
+++ b/models/ngspice/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py
@@ -0,0 +1,140 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from cmath import inf
+from re import L, T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device, table):
+
+ # Generate CSVs with truth tables
+ df = pd.DataFrame(data=table)
+ df.set_index(df.columns[0])
+ new_header = df.iloc[0]
+ df = df[1:]
+ df.columns = new_header
+ df.to_csv(f"{device}/{device}_measured.csv", index = False)
+
+def ext_simulated(device, processes, volts, temps):
+
+ # Get all corners simulated
+ for process in processes:
+ for volt in volts:
+ for temp in temps:
+ with open(f"device_netlists/gf180mcu_fd_sc_mcu7t5v0__{device}_1.spice") as f:
+ tmpl = Template(f.read())
+ netlist_path = f"{device}/{device}_netlists/{device}_{process}_{temp}c_{volt}v.spice"
+ with open(netlist_path, "w") as netlist:
+ netlist.write(tmpl.render(process = process, volt = volt , temp = temp ))
+
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ df_simulated = pd.read_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv",header=None, delimiter=r"\s+")
+ results = []
+ for i in df_simulated.columns:
+ if df_simulated.iloc[0, i] > 2.5:
+ results.append(1)
+ else:
+ results.append(0)
+ df_measured = pd.read_csv(f"{device}/{device}_measured.csv",header=0)
+ df = df_measured
+ df_measured.drop(df_measured.columns[len(df_measured.columns)-1], axis=1, inplace=True)
+ df_measured['output'] = results[1::2]
+ df_measured.to_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv",index= False)
+
+def error_cal(device, processes, volts, temps):
+
+ print (f"\nSimulation results of {device}")
+ measured = pd.read_csv(f"{device}/{device}_measured.csv")
+ for process in processes:
+ for volt in volts:
+ for temp in temps:
+ simulated = pd.read_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv")
+
+ res = (measured == simulated).all().all()
+ print ("{:^5s} in PVT of {:^7s}, {:^3s}V, {:^3s}C functional simulation = {}".format(device, process, volt, temp, res))
+ print ("================================================================================================\n")
+
+def main():
+
+ devices = ["inv","nand2","or3"]
+
+ # Generate truth tables data
+ inv_table = [["input","output"],
+ [0,1],
+ [1,0]]
+
+ nand2_table = [["input1","input2","output"],
+ [0,0,1],
+ [0,1,1],
+ [1,0,1],
+ [1,1,0]]
+
+ or3_table = [["input1","input2","input3","output"],
+ [0,0,0,0],
+ [0,0,1,1],
+ [0,1,0,1],
+ [0,1,1,1],
+ [1,0,0,1],
+ [1,0,1,1],
+ [1,1,0,1],
+ [1,1,1,1]]
+
+ tables = [inv_table,nand2_table,or3_table]
+
+ processes = ["typical","ff","ss"]
+ volts = ["5", "4.5", "5.5"]
+ temps = ["25", "-40", "125"]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ if os.path.exists(device) and os.path.isdir(device):
+ shutil.rmtree(device)
+ os.makedirs(device)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/{device}_netlists",exist_ok=True)
+ os.makedirs(f"{device}/simulated",exist_ok=True)
+
+ ext_measured (device, tables[i])
+ # =========== Simulate ==============
+ ext_simulated(device, processes, volts, temps)
+
+ # ============ Results ==============
+ error_cal (device, processes, volts, temps)
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/ngspice/testing/smoke_test/.spiceinit b/models/ngspice/testing/smoke_test/.spiceinit
new file mode 100644
index 0000000..6fc9af8
--- /dev/null
+++ b/models/ngspice/testing/smoke_test/.spiceinit
@@ -0,0 +1,2 @@
+************
+set ngbehavior=hs
diff --git a/models/ngspice/testing/smoke_test/inv_ng.spice b/models/ngspice/testing/smoke_test/inv_ng.spice
new file mode 100644
index 0000000..5a3b4c9
--- /dev/null
+++ b/models/ngspice/testing/smoke_test/inv_ng.spice
@@ -0,0 +1,44 @@
+**.subckt INV_tb
+* Copyright 2022 Efabless Corporation
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+Vdd Vdd 0 3.3
+Vin Vin 0 dc pulse(0 3.3 0 1p 1p 100p 200p)
+X1 Vdd Vout Vin 0 INV
+
+
+.temp {{temp}}
+.options tnom={{temp}}
+
+
+.control
+tran 1p 400p
+meas tran tphl TRIG v(Vin) VAL={0.5*3.3} RISE=2 TARG v(Vout) VAL={0.5*3.3} FALL=2
+meas tran tplh TRIG v(Vin) VAL={0.5*3.3} FALL=1 TARG v(Vout) VAL={0.5*3.3} RISE=1
+print {(tplh+tphl)/2}
+wrdata {{run_path}}/simulation/inv_W{{width}}_L{{length}}_T{{temp}}_{{corner}}.csv {(tplh+tphl)/2}
+.endc
+
+
+.include "../../design.ngspice"
+.lib "../../sm141064.ngspice" {{corner}}
+
+
+.subckt INV VDD Vout Vin GND
+XM1 Vout Vin GND GND nmos_3p3 W={{width}}u L={{length}}u
+*ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+XM2 Vout Vin VDD VDD pmos_3p3 W = {{width_p}}u L = {{length}}u
+*ad={{AD_p}}u pd={{PD_p}}u as={{AD_p}}u ps={{PD_p}}u
+.ends
+
+.end
\ No newline at end of file
diff --git a/models/ngspice/testing/smoke_test/ng_smoke_test.py b/models/ngspice/testing/smoke_test/ng_smoke_test.py
new file mode 100644
index 0000000..23128ee
--- /dev/null
+++ b/models/ngspice/testing/smoke_test/ng_smoke_test.py
@@ -0,0 +1,120 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Usage:
+ smoke_test.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+import re
+from docopt import docopt
+import pandas as pd
+import os
+from jinja2 import Template
+import concurrent.futures
+import itertools
+import datetime
+import warnings
+import subprocess
+
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ p = subprocess.Popen(f"ngspice -b -a {file_name} > {file_name}.log 2>&1", shell=True, executable='/bin/bash')
+ p.wait()
+
+def get_sizes(models_path):
+ with open(models_path, "r") as f:
+ device_model = f.read()
+ dimensions = re.findall(f"\.model nmos_3p3.*\n.*\n\+lmin.*= (.*\S).*\n.*\n\+wmin.*= (.*\S)", device_model)
+ return dimensions[0:16]
+
+def get_results(run_path, sizes, temp, corner):
+ netlist_tmp = f"./inv_ng.spice"
+ width = float(sizes[1]) * 1000000
+ width_p = width * 1.5
+ length = float(sizes[0]) * 1000000
+ # AD = width * 0.24
+ # AD_p = AD * 1.5
+ # PD = 2 * (width + 0.24)
+ # PD_p = width + PD
+ # AS = AD
+ # PS = PD
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+
+ os.makedirs(f"{run_path}/netlists",exist_ok=True)
+ os.makedirs(f"{run_path}/simulation",exist_ok=True)
+ netlist_path = f"{run_path}/netlists/inv_W{width}_L{length}_T{temp}_{corner}.spice"
+ with open(netlist_path, "w") as netlist:
+ netlist.write(tmpl.render(corner = corner, width = width,length = length, temp = temp , run_path = run_path, width_p = width_p))#, AD = AD , PD = PD , AS = AS , PS = PS, AD_p = AD_p, PD_p = PD_p ))
+
+ call_simulator(netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{run_path}/simulation/inv_W{width}_L{length}_T{temp}_{corner}.csv",header=None, delimiter=r"\s+")
+ return [f"W{width}_L{length}_T{temp}_{corner}",df_simulated.iloc[-1, -1]]
+
+def main():
+
+ models_path = "../../sm141064.ngspice"
+ temps = ["25","-40","125"]
+ corners = ["typical","ff","ss","fs","sf"]#,"stat"]
+
+ time = f"{datetime.datetime.now()}".replace(" ", "_")
+ run_path = f"../run_smoke_{time}"
+ os.makedirs(run_path,exist_ok=True)
+
+ sizes = get_sizes(models_path)
+ results = []
+
+ all_combs = list(itertools.product(sizes, temps, corners))
+
+ with concurrent.futures.ThreadPoolExecutor(max_workers=workers_count) as executor:
+ # Start the load operations and mark each future with its URL
+ future_list = [executor.submit(get_results, run_path, comb[0], comb[1], comb[2]) for comb in all_combs]
+ for future in concurrent.futures.as_completed(future_list):
+ try:
+ results.append(future.result())
+ except Exception as exc:
+ print('Generated an exception: %s' % (exc))
+
+
+ df_results = pd.DataFrame(results)
+ df_results.columns = ["run","tpd_result"]
+ df_results.to_csv(f"{run_path}/final_results.csv",index= False)
+
+ print (df_results)
+
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='smoke_test: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx
new file mode 100644
index 0000000..4910b80
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_npn.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx
new file mode 100644
index 0000000..045b1a3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_cv_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx
new file mode 100644
index 0000000..054b06a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_npn.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx
new file mode 100644
index 0000000..d9db37a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_fc_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx
new file mode 100644
index 0000000..d8d27d0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_npn.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx
new file mode 100644
index 0000000..2492ba0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mc_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx
new file mode 100644
index 0000000..6ac6163
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_mm_pnp.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx
new file mode 100644
index 0000000..2c7d454
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx
new file mode 100644
index 0000000..1b769d4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_beta_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx
new file mode 100644
index 0000000..f5a1c99
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx
new file mode 100644
index 0000000..6ac941a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_flyback_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx
new file mode 100644
index 0000000..4989747
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx
new file mode 100644
index 0000000..26898b2
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_gummel_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx
new file mode 100644
index 0000000..96b07d9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx
new file mode 100644
index 0000000..76d04f4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_npn_icvc_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx
new file mode 100644
index 0000000..a80ee45
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx
new file mode 100644
index 0000000..335d907
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_beta_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx
new file mode 100644
index 0000000..364ca47
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx
new file mode 100644
index 0000000..ac6108e
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_flyback_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx
new file mode 100644
index 0000000..af592f1
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx
new file mode 100644
index 0000000..443c2de
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_gummel_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx
new file mode 100644
index 0000000..8fa1a77
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_f.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx
new file mode 100644
index 0000000..fec8e82
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/BJT/bjt_pnp_icvc_r.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx
new file mode 100644
index 0000000..1b61641
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx
new file mode 100644
index 0000000..0ef13b4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Cap/mimcap_mc.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx
new file mode 100644
index 0000000..4711b88
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx
new file mode 100644
index 0000000..3585f1a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx
new file mode 100644
index 0000000..5582df3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx
new file mode 100644
index 0000000..d9d62db
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwps_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx
new file mode 100644
index 0000000..ccd2908
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx
new file mode 100644
index 0000000..25d7edf
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/dnwpw_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..cbf4db3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..f4527e0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..7023e41
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..08807f6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/np_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..1b2af92
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..0c964d8
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..5789d79
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9321fb6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/nwp_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..bed9727
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..c45be6c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..2fc2a7c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9cbce9b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/pn_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6767fc
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx
new file mode 100644
index 0000000..8b9344f
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Diode/sc_diode_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx
new file mode 100644
index 0000000..efd1039
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/hvfet_spec.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx
new file mode 100644
index 0000000..c763f6b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx
new file mode 100644
index 0000000..0d975fe
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx
new file mode 100644
index 0000000..674174c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx
new file mode 100644
index 0000000..bbadf6c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/nmos_10p0_asym_scaling_trend.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx
new file mode 100644
index 0000000..dfd13b6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx
new file mode 100644
index 0000000..8e5eb30
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx
new file mode 100644
index 0000000..ca38856
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx
new file mode 100644
index 0000000..b2f245d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/pmos_10p0_asym_scaling_trend.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx
new file mode 100644
index 0000000..d337742
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_1sig.nl.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx
new file mode 100644
index 0000000..c6d8642
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_2sig.nl.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx
new file mode 100644
index 0000000..bc8639c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/HV_FET/stat_mc_np_correlation_3sig.nl.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..813635c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..fd139e1
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/3p3_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6d80ee
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx
new file mode 100644
index 0000000..407129c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_nat_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..1dbd85c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/6p0_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..b6b74f9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx
new file mode 100644
index 0000000..dd48b9d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..cba86be
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..5ceb2a6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..b4b36f9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_3p3_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..9842b38
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx
new file mode 100644
index 0000000..3a70d17
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx
new file mode 100644
index 0000000..ab51a89
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_nat_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx
new file mode 100644
index 0000000..3662177
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..3aee704
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..bb8e4f4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..865ff15
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/nmos_6p0_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx
new file mode 100644
index 0000000..2fa2797
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx
new file mode 100644
index 0000000..f7ba8ac
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..d87ad5d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..c7cf1cc
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..16c3a3a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_3p3_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx
new file mode 100644
index 0000000..a9254cb
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx
new file mode 100644
index 0000000..aebf3e1
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx
new file mode 100644
index 0000000..a824873
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx
new file mode 100644
index 0000000..10eaca3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_iv_dsab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx
new file mode 100644
index 0000000..8cb3a2d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/pmos_6p0_sab_noi.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..1416ab9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..89a74f0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..e7085f0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..ad30f91
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_nmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..19118d4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..48791d7
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..a85c6c3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..be21ff6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/scaling_pmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx
new file mode 100644
index 0000000..3ef61df
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/spec_all_fets.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx
new file mode 100644
index 0000000..0f18273
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..a3030a1
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx
new file mode 100644
index 0000000..f191aaa
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..3262949
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx
new file mode 100644
index 0000000..a4830f9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mc_nmos_6p0_nat.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..51142f0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..4cafde6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..274347d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..a139dce
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_nmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx
new file mode 100644
index 0000000..86f4631
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx
new file mode 100644
index 0000000..c4bd383
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_3p3_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx
new file mode 100644
index 0000000..b9314cc
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx
new file mode 100644
index 0000000..c081636
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/MOS/stat_mm_pmos_6p0_sab.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..bb5c8e7
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01a-wl-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..84cfe2f
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nom-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..57c0b79
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01b-temp-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..623e9ba
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01c-mm-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..40744c4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01d-mc-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx
new file mode 100644
index 0000000..cb09382
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES01e-cvcor-nplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..0dbd278
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02a-wl-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..20315b0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-nom-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..2df4e8f
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02b-temp-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..5640c40
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02c-mm-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..4ba2549
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02d-mc-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx
new file mode 100644
index 0000000..10b193a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES02e-cvcor-pplus_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..260a8ca
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03a-wl-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..e65b1fc
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03b-temp-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..ff7ad36
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03d-mc-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx
new file mode 100644
index 0000000..76fea8b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES03e-cvcor-nplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..1a49e0c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04a-wl-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..0fbb672
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04b-temp-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..6e0a4b1
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04d-mc-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx
new file mode 100644
index 0000000..b043825
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES04e-cvcor-pplus_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx
new file mode 100644
index 0000000..6408995
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05a-wl-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx
new file mode 100644
index 0000000..7cbd776
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05b-temp-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx
new file mode 100644
index 0000000..12f2d07
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES05e-cvcor-nwell.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..0d06e5e
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06a-wl-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..2687b8c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06b-temp-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..c624220
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06c-mm-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..e06c435
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06d-mc-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..d48576b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06e-cvcor-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..fa0909e
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES06f-noise-npolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..479a205
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07a-wl-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..6d890fa
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07b-temp-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..fba4fbc
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07c-mm-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..31fcb16
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07d-mc-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..a4188bd
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07e-cvcor-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx
new file mode 100644
index 0000000..a26d9ba
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES07f-noise-ppolyf_u.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..64025b9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08a-wl-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..d9c246d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08b-temp-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..5bb2231
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08d-mc-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..77df6d2
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08e-cvcor-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..da255df
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES08f-noise-npolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..6896518
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09a-wl-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..04bb357
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09b-temp-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..d7d005b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09d-mc-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..02146f8
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09e-cvcor-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx
new file mode 100644
index 0000000..2d1275d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES09f-noise-ppolyf_s.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..7c03c8a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10a-wl-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..de70746
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10b-temp-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx
new file mode 100644
index 0000000..758a7f4
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10d-mc-ppolyf_u_1k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx
new file mode 100644
index 0000000..76c959a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10e-cvcor-ppolyf_u_1k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx
new file mode 100644
index 0000000..5ad8dfe
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES10f-noise-ppolyf_u_1k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..71aa86d
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11a-wl-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..1e91eb0
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11b-temp-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx
new file mode 100644
index 0000000..8347b1c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11d-mc-ppolyf_u_2k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx
new file mode 100644
index 0000000..4b15b25
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11e-cvcor-ppolyf_u_2k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx
new file mode 100644
index 0000000..430db7b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES11f-noise-ppolyf_u_2k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..bbcecca
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12a-wl-ppolyf_u_1k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx
new file mode 100644
index 0000000..6632263
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12b-temp-ppolyf_u_1k_6p0.nl.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..0c2cd97
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12d-mc-ppolyf_u_1k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx
new file mode 100644
index 0000000..26afc55
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12e-cvcor-ppolyf_u_1k_6p0.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx
new file mode 100644
index 0000000..d587bc9
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES12f-noise-ppolyf_u_1k_6p0.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..75bb7db
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13a-wl-ppolyf_u_2k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx
new file mode 100644
index 0000000..f258c60
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13b-temp-ppolyf_u_2k_6p0.nl.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx
new file mode 100644
index 0000000..f303906
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13d-mc-ppolyf_u_2k_6p0.nl_o.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx
new file mode 100644
index 0000000..7fb2330
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13e-cvcor-ppolyf_u_2k_6p0.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx
new file mode 100644
index 0000000..731c3ce
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES13f-noise-ppolyf_u_2k_6p0.n.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..e11f941
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14a-wl-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..40b1ff2
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14b-temp-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx
new file mode 100644
index 0000000..332dbc3
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14d-mc-ppolyf_u_3k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx
new file mode 100644
index 0000000..6386669
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14e-cvcor-ppolyf_u_3k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx
new file mode 100644
index 0000000..359b06c
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES14f-noise-ppolyf_u_3k.nl_ou.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx
new file mode 100644
index 0000000..597ed2a
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15a-wl-rm1.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx
new file mode 100644
index 0000000..7af7d6b
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES15b-temp-rm1.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx
new file mode 100644
index 0000000..8e224e2
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16a-wl-rm2.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx
new file mode 100644
index 0000000..7400b88
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES16b-temp-rm2.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx
new file mode 100644
index 0000000..7d5f551
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17a-wl-rm3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx
new file mode 100644
index 0000000..18592fa
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES17b-temp-rm3.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx
new file mode 100644
index 0000000..214f560
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18a-wl-tm6k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx
new file mode 100644
index 0000000..acbbf89
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES18b-temp-tm6k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx
new file mode 100644
index 0000000..c7701c8
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19a-wl-tm9k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx
new file mode 100644
index 0000000..88ec343
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES19b-temp-tm9k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx
new file mode 100644
index 0000000..b414848
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20a-wl-tm11k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx
new file mode 100644
index 0000000..5dc8f38
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES20b-temp-tm11k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx
new file mode 100644
index 0000000..8639044
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21a-wl-tm30k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx
new file mode 100644
index 0000000..aab88bd
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/RES21b-temp-tm30k.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx
new file mode 100644
index 0000000..596dfb6
--- /dev/null
+++ b/models/xyce/testing/180MCU_SPICE_DATA/Resistor/efuse.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/Makefile b/models/xyce/testing/Makefile
new file mode 100644
index 0000000..7cc3ce2
--- /dev/null
+++ b/models/xyce/testing/Makefile
@@ -0,0 +1,183 @@
+# Copyright 2022 Efabless Corporation
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+#=================================================================
+# ------------------------ models_xyce ------------------------
+#=================================================================
+
+SHELL := /bin/bash
+Testing_DIR ?= $(shell pwd)
+run_folder := $(shell date +'run_%Y_%m_%d_%H:%M:%S')
+
+
+.DEFAULT_GOAL := all
+
+all : test-models_xyce
+
+test-models_xyce: smoke-test models-MOS models-BJT models-diode models-MOSCAP models-MIMCAP models-RES
+
+#================================
+#---------- Create Run ----------
+#================================
+
+.ONESHELL:
+Add_run-dir:
+ @cd $(Testing_DIR)
+ @ [ ! -d "$(run_folder)/" ] && cp -rf $(Testing_DIR)/regression $(Testing_DIR)/$(run_folder)
+
+#================================
+#---------- smoke-test ----------
+#================================
+
+.ONESHELL:
+smoke-test:
+ @cd $(Testing_DIR)/smoke_test
+ @python3 xyce_smoke_test.py
+
+
+#================================
+# ---------- models-MOS----------
+#================================
+
+.ONESHELL:
+models-MOS: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/mos_iv_vgs
+ @echo "========== Runing models_xyce-MOS regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+# @cd ../mos_iv_vbs
+# @ python3 models_regression.py |& tee -a ../run_log.log
+
+# .ONESHELL:
+# clean-models-MOS:
+# @echo "==== Cleaning models-MOS old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/mos_iv_vgs && rm -rf nmos_* pmos_*
+
+#================================
+# ---------- models-BJT----------
+#================================
+
+.ONESHELL:
+models-BJT: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/bjt_cj
+ @echo "========== Runing models_xyce-BJT regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-BJT:
+# @echo "==== Cleaning models-BJT old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/bjt_cj && rm -rf vnpn_* vpnp_*
+
+#================================
+# --------- models-diode --------
+#================================
+
+.ONESHELL:
+models-diode: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/diode
+ @echo "========== Runing models_xyce-diode regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-diode:
+# @echo "==== Cleaning models-diode old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/diode && rm -rf np_* pn_* nwp* dnw* sc_*
+
+#================================
+# -------- models-MOSCAP --------
+#================================
+
+.ONESHELL:
+models-MOSCAP: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/moscap_c
+ @echo "========== Runing models_xyce-MOSCAP regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-MOSCAP:
+# @echo "==== Cleaning models-MOSCAP old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/moscap_c && rm -rf nmoscap_* pmoscap_*
+
+#================================
+# -------- models-MIMCAP --------
+#================================
+
+.ONESHELL:
+models-MIMCAP: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/mimcap_c
+ @echo "========== Runing models_xyce-MIMCAP regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-MIMCAP:
+# @echo "==== Cleaning models-MIMCAP old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/mimcap_c && rm -rf mim*
+
+
+#================================
+# --------- models-RES ----------
+#================================
+
+.ONESHELL:
+models-RES: Add_run-dir
+ @cd $(Testing_DIR)/$(run_folder)/resistor_r
+ @echo "========== Runing models_xyce-RES regression ==========" |& tee -a ../run_log.log
+ @ python3 models_regression.py |& tee -a ../run_log.log
+
+
+# .ONESHELL:
+# clean-models-RES:
+# @echo "==== Cleaning models-RES old runs ===="
+# @cd $(Testing_DIR)/$(run_folder)/resistor_r && rm -rf nplus* pplus* npoly* ppoly* rm* tm* nwell
+
+
+#===============================
+# --------- Clean ALL ----------
+#===============================
+
+# .ONESHELL:
+# clean: clean-models-MOS clean-models-BJT clean-models-diode clean-models-MOSCAP clean-models-MIMCAP clean-models-RES
+# @echo "==== Cleaning all runs is done ===="
+
+
+#==========================
+# --------- HELP ----------
+#==========================
+
+# Help Target
+help:
+ @echo "\n ==== The following are some of the valid targets for this Makefile ====\n"
+ @echo "... all (the default if no target is provided)"
+ @echo "... smoke-test (To run smoke test for an inverter)"
+ @echo "... test-models_xyce (To run regression for all devices)"
+ @echo "... models-MOS (To run regression for MOS devices)"
+ @echo "... models-BJT (To run regression for BJT devices)"
+ @echo "... models-diode (To run regression for diode devices)"
+ @echo "... models-MOSCAP (To run regression for MOSCAP devices)"
+ @echo "... models-MIMCAP (To run regression for MIMCAP devices)"
+ @echo "... models-RES (To run regression for RES devices)"
+# @echo "... clean-models-MOS (To clean old runs for MOS devices)"
+# @echo "... clean-models-BJT (To clean old runs for BJT devices)"
+# @echo "... clean-models-diode (To clean old runs for diode devices)"
+# @echo "... clean-models-MOSCAP (To clean old runs for MOSCAP devices)"
+# @echo "... clean-models-MIMCAP (To clean old runs for MIMCAP devices)"
+# @echo "... clean-models-RES (To run regression for RES devices)"
+
+# @echo "... clean (To clean all old runs) "
+
+.PHONY : help
\ No newline at end of file
diff --git a/models/xyce/testing/README.md b/models/xyce/testing/README.md
new file mode 100644
index 0000000..063cc25
--- /dev/null
+++ b/models/xyce/testing/README.md
@@ -0,0 +1,51 @@
+# Globalfoundries 180nm MCU models-xyce regression
+
+Explains how to run GF180nm models-xyce regression.
+
+## Folder Structure
+
+```text
+📦testing
+ ┣ 📜Makefile
+ ┣ 📜README.md
+ ┣ 📦regression
+ ┣ 📦smoke_test
+ ┣ 📦180MCU_SPICE_Models
+ ```
+
+## Prerequisites
+
+At a minimum:
+
+- Git 2.35+
+- Python 3.6+
+- Xyce 7.5+
+
+### On Ubuntu, you can just
+
+`apt install -y build-essential python3`
+
+- Check this [xyce](https://xyce.sandia.gov/documentation-tutorials/building-guide/) for Xyce installation.
+
+## Regression Usage
+
+To make a full test for GF180nm models-xyce, you could use the following command in testing directory:
+
+```bash
+make all
+```
+
+- You could also check allowed targets in the Makefile, using the following command:
+
+ ```bash
+ make help
+ ```
+
+## **Regression Outputs**
+
+- The resulting files are in `regression/<device_folder>/` with name of `<device_name><options>` that contains:
+
+ 1. A final report file of all results.
+ 2. measured folder that contains measured data used in regression.
+ 3. simulated folder that contains simulated data used in regression.
+ 4. netlists folder that contains spice files used in simulation.
diff --git a/models/xyce/testing/regression/bjt_beta/device_netlists/npn.spice b/models/xyce/testing/regression/bjt_beta/device_netlists/npn.spice
new file mode 100644
index 0000000..ad9e216
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_beta/device_netlists/npn.spice
@@ -0,0 +1,26 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 3
+Vbp b 0 dc 1.2
+
+xq1 c b 0 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.DC Vbp 0.2 1.2 0.01 Vcp 1 3 1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=npn/simulated_Ic/{{i}}_simulated_{{device}}.csv Vbp
+.print DC FORMAT=CSV file=npn/simulated_Ic/{{i}}_simulated_{{device}}.csv {-I(Vcp)}
+.print DC FORMAT=CSV file=npn/simulated_Ib/{{i}}_simulated_{{device}}.csv Vbp
+.print DC FORMAT=CSV file=npn/simulated_Ib/{{i}}_simulated_{{device}}.csv {-I(Vbp)}
+
+
+.include "../../../design.xyce"
+.lib "../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_beta/device_netlists/pnp.spice b/models/xyce/testing/regression/bjt_beta/device_netlists/pnp.spice
new file mode 100644
index 0000000..f238b9c
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_beta/device_netlists/pnp.spice
@@ -0,0 +1,26 @@
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc -3
+Vbp b 0 dc -1.2
+
+xq1 c b 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+
+.DC Vbp -0.2 -1.2 -0.01 Vcp -1 -3 -1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pnp/simulated_Ic/{{i}}_simulated_{{device}}.csv Vbp
+.print DC FORMAT=CSV file=pnp/simulated_Ic/{{i}}_simulated_{{device}}.csv {I(Vcp)}
+.print DC FORMAT=CSV file=pnp/simulated_Ib/{{i}}_simulated_{{device}}.csv Vbp
+.print DC FORMAT=CSV file=pnp/simulated_Ib/{{i}}_simulated_{{device}}.csv {I(Vbp)}
+
+
+.include "../../../design.xyce"
+.lib "../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice b/models/xyce/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice
new file mode 100644
index 0000000..60e160e
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_beta/device_netlists/run_npn_beta.spice
@@ -0,0 +1,25 @@
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 3
+Vbp b 0 dc 1.2
+
+xq1 c b 0 0 vnpn_10x10
+
+
+*****************
+** Analysis
+*****************
+.DC Vbp 0.2 1.2 0.01 Vcp 1 3 1
+.STEP TEMP 25 -60 200
+.print DC FORMAT=CSV file=result0.csv Vbp
+.print DC FORMAT=CSV file=result0.csv {-I(Vbp)}
+.print DC FORMAT=CSV file=result1.csv Vbp
+.print DC FORMAT=CSV file=result1.csv {-I(Vcp)}
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_beta/models_regression.py b/models/xyce/testing/regression/bjt_beta/models_regression.py
new file mode 100644
index 0000000..9bdc7cd
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_beta/models_regression.py
@@ -0,0 +1,243 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from cmath import inf
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vb,step,Id_sim,list_devices,vc):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+
+ # Extracting measured values for each Device
+ for i in range (loops):
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ # Special case for 1st measured values
+ if i == 0 :
+ if device == "pnp":
+ temp_vb = vb
+ vb = "-vb "
+ # measured Id_sim 0
+ col_list = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[0]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+ if device == "pnp":
+ vb = temp_vb
+
+ # measured Id_sim 1
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i+1}",f"{vc}{step[1]}.{2*i+1}",f"{vc}{step[2]}.{2*i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[1]}/{i}_measured_{list_devices[k]}.csv", index = False)
+ else:
+ # measured Id_sim 0
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i}",f"{vc}{step[1]}.{2*i}",f"{vc}{step[2]}.{2*i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[0]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+ # measured Id_sim 1
+ col_list = [f"{vb}",f"{vc}{step[0]}.{2*i+1}",f"{vc}{step[1]}.{2*i+1}",f"{vc}{step[2]}.{2*i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vb}",f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim[1]}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+def ext_simulated(device,vc,step,sweep,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ netlist_tmp = f"./device_netlists/{device}.spice"
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_sim",exist_ok=True)
+ with open(f"{device}/{device}_netlists_sim/{i}_{device}_netlist_{list_devices[k]}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = list_devices[k], i = i , temp = temp ))
+ netlist_path = f"{device}/{device}_netlists_sim/{i}_{device}_netlist_{list_devices[k]}.spice"
+
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data 0
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",header=0)
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 1]
+
+ # Writing final simulated data 0
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[0]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+
+ # Writing simulated data 1
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",header=0)
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 1]
+
+ # Writing final simulated data 1
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim[1]}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+def error_cal(device,vb,step,Id_sim,list_devices,vc):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ measured = pd.read_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[0:, 1]) - abs(simulated.iloc[0:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[0:, 2]) - abs(simulated.iloc[0:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[0:, 3]) - abs(simulated.iloc[0:, 3]))/abs(measured.iloc[:, 3])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3]).transpose()
+ df_error.replace([np.inf, -np.inf], df_error.max().nlargest(2).iloc[1], inplace=True)
+ df_error.to_csv(f"{device}/error_{Id_sim}/{i}_{device}_error_{list_devices[k]}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{vc}{step[0]}"].mean() + df_error[f"{vc}{step[1]}"].mean() + df_error[f"{vc}{step[2]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"{vc}{step[0]}",f"{vc}{step[1]}",f"{vc}{step[2]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vc = (df_error == max_error).idxmax(axis=1)[max_index]
+ if Id_sim == "Ic":
+ if i == 0 :
+ if device == "pnp":
+ temp_vb = vb
+ vb = "-vb "
+ else:
+ if device == "pnp":
+ vb = temp_vb
+ max_location_vb = df_error[f"{vb}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'device': f'{list_devices[k]}','Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vc} & Vc (V) = {max_location_vb}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["npn","pnp"]
+ list_devices = [["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4", "vnpn_0p54x2"],
+ ["vpnp_0p42x10" , "vpnp_0p42x5", "vpnp_10x10" , "vpnp_5x5"]]
+ vb = ["vbp ","-vb (V)"]
+ vc = ["vcp =","vc =-"]
+ Id_sim = ["Ic","Ib"]
+ sweep = 101
+ step = [1, 2, 3]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{Id_sim[1]}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/bjt_{device}_beta_f.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim[0]}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{Id_sim[1]}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim[1]}",exist_ok=False)
+
+ # =========== Simulate ==============
+ ext_measured (device,vb[i],step,Id_sim,list_devices[i],vc[i])
+
+ ext_simulated(device,vb[i],step,sweep,Id_sim,list_devices[i],vc[i])
+
+ # ============ Results =============
+ error_cal (device,vb[i],step,Id_sim[0],list_devices[i],vc[i])
+ error_cal (device,vb[i],step,Id_sim[1],list_devices[i],vc[i])
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/bjt_cj/device_netlists/cv.spice b/models/xyce/testing/regression/bjt_cj/device_netlists/cv.spice
new file mode 100644
index 0000000..92eed52
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_cj/device_netlists/cv.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+.param volt = 0
+V1 in 0 dc {volt} ac 1
+{{Iopen}}
+
+R1 in out 1G
+xq1 {{connection}} {{device}}
+
+.meas AC freq when Vdb(out)=-3 PRECISION=15
+
+*****************
+** Analysis
+*****************
+.ac dec 10 1 10G
+.step volt 0 -3.0 -0.1
+.STEP TEMP {{temp}} -60 200
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" bjt_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/bjt_cj/device_netlists/run_npn_cv.spice b/models/xyce/testing/regression/bjt_cj/device_netlists/run_npn_cv.spice
new file mode 100644
index 0000000..3b6d488
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_cj/device_netlists/run_npn_cv.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+.param volt = -3.0
+V1 in 0 dc {volt} ac 1
+*Iopen open 0 0
+
+R1 in out 1G
+xq1 0 out 0 0 vnpn_10x10
+
+.meas AC freq when Vdb(out)=-3 PRECISION=15
+
+*****************
+** Analysis
+*****************
+.ac dec 10 1 10G
+.STEP volt 0 -3.0 -0.1
+.STEP TEMP 25 -60 200
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_cj/device_netlists/run_pnp_cv.spice b/models/xyce/testing/regression/bjt_cj/device_netlists/run_pnp_cv.spice
new file mode 100644
index 0000000..d64aa7e
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_cj/device_netlists/run_pnp_cv.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+.param volt = 0
+V1 in 0 dc {volt} ac 1
+* Iopen 0 open 0
+
+R1 in out 1G
+xq1 out 0 0 vpnp_0p42x10
+
+.meas AC freq when Vdb(out)=-3 PRECISION=15
+
+*****************
+** Analysis
+*****************
+.ac dec 10 1 10G
+.STEP volt 0 -3.0 -0.1
+* .STEP TEMP 25 -60 200
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" bjt_typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/bjt_cj/models_regression.py b/models/xyce/testing/regression/bjt_cj/models_regression.py
new file mode 100644
index 0000000..3954d94
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_cj/models_regression.py
@@ -0,0 +1,257 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start,dirpath):
+
+ # Get dimensions used for each device
+ if "vnpn" in device:
+ loops = 3
+ else:
+ loops = 2
+
+ # Extracting measured values for each W & L
+ for i in range (start,loops+start):
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_{device}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_{device}.csv", index = False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,temp,dirpath,cap):
+
+ # Get dimensions used for each device
+ netlist_tmp = f"./device_netlists/cv.spice"
+
+ if "EBJ" in cap:
+ i = 0
+ if "vnpn" in device:
+ connection = "0 out 0 0"
+ else:
+ connection = "0 0 out"
+ Iopen = ""
+ elif "CBJ" in cap:
+ i = 1
+ if "vnpn" in device:
+ connection = "0 out open 0"
+ Iopen = "Iopen open 0 0"
+ else:
+ connection = "out 0 0"
+ Iopen = ""
+ else:
+ i =2
+ connection = "0 0 open out"
+ Iopen = "Iopen open 0 0"
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i}_{device}_netlist_{device}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, i = i , Iopen = Iopen , connection = connection , temp = temp, cv_sim = cv_sim , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i}_{device}_netlist_{device}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = []
+ # Writing simulated data
+ for j in range(len([x for x in os.listdir(f"{dirpath}/{device}_netlists_{cv_sim}") if f"{i}_{device}_netlist_{device}.spice.ma" in x])):
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i}_{device}_netlist_{device}.spice.ma{j}") as f:
+ caps = 1000000/(float(next(f).replace("FREQ = ", ""))*2*np.pi)
+ df_simulated.append(caps)
+
+ # zero array to append in it shaped (vn_sweeps, number of trials + 1)
+ new_array = np.zeros((len(df_simulated), 2))
+ new_array[:len(df_simulated), 0] = df_simulated
+ new_array[:len(df_simulated), 1] = df_simulated
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ if "npn_EBJ" in cap:
+ df_cbj = pd.read_csv(f"{dirpath}/simulated_{cv_sim}/1_simulated_{device}.csv")
+ diff = df_simulated.iloc[:, 1] - df_cbj.iloc[:, 1]
+ df_simulated = pd.DataFrame(data=[df_simulated.iloc[:, 0],diff]).transpose()
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",index= False)
+ elif "pnp_CBJ" in cap:
+ df_ebj = pd.read_csv(f"{dirpath}/simulated_{cv_sim}/0_simulated_{device}.csv")
+ diff = df_simulated.iloc[:, 1] - (2*df_ebj.iloc[:, 1])
+ df_simulated = pd.DataFrame(data=[df_simulated.iloc[:, 0],diff]).transpose()
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",index= False)
+ else:
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv",index= False)
+
+
+def error_cal(device,vn,d_in,cv_sim, corner,temp,dirpath):
+
+ # Get dimensions used for each device
+ if "vnpn" in device:
+ loops = 3
+ else:
+ loops = 2
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ if i == 0 : cap = "EBJ"
+ elif i == 1 : cap = "CBJ"
+ else : cap = "CSJ"
+
+ measured = pd.read_csv(f"{dirpath}/measured_{cv_sim}/{i}_measured_{device}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{cv_sim}/{i}_simulated_{device}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{cv_sim}/{i}_{device}_error_{device}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}_{corner}"].mean())
+ # Max error
+ max_error = df_error[f"{d_in}_{corner}"].max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vn}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'corner': f'{corner}' , 'Junction': f'{cap}', 'Simulated_Val':f'{cv_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vn (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{cv_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{cv_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ corners = ["typical","ff","ss"]
+ temps = [ 25 , -40 , 175 ]
+ measure = ["cv","Vj", "bjt", 31]
+ cv_sim, bjt_vn, bjt_in = measure[0], measure[1], measure[2]
+
+ #vnpn
+ npn_devices = ["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4" , "vnpn_0p54x2"]
+ npn_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in npn_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/{bjt_in}_{cv_sim}_npn.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+ ext_measured (device,bjt_vn,bjt_in, cv_sim, corner,npn_start,dirpath)
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_CBJ")
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_EBJ")
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"npn_CSJ")
+
+ npn_start = npn_start + 3
+ npn_start = 0
+
+ # vpnp
+ pnp_devices = ["vpnp_0p42x10" , "vpnp_0p42x5" , "vpnp_10x10" , "vpnp_5x5"]
+ pnp_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in pnp_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/{bjt_in}_{cv_sim}_pnp.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+ ext_measured (device,bjt_vn,bjt_in, cv_sim, corner,pnp_start,dirpath)
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"pnp_EBJ")
+ ext_simulated(device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath,"pnp_CBJ")
+ error_cal (device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath)
+
+ pnp_start = pnp_start + 2
+ pnp_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in npn_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ error_cal (device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath)
+
+ npn_start = npn_start + 3
+ npn_start = 0
+
+ # vpnp
+ pnp_devices = ["vpnp_0p42x10" , "vpnp_0p42x5" , "vpnp_10x10" , "vpnp_5x5"]
+ pnp_start = 0
+
+ for corner in corners:
+ for temp in temps:
+ for device in pnp_devices:
+ # Folder structure of measured values
+ dirpath = f"{device}_{cv_sim}_{corner}_T{temp}"
+ error_cal (device,bjt_vn,bjt_in,cv_sim, corner,temp,dirpath)
+
+ pnp_start = pnp_start + 2
+ pnp_start = 0
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/bjt_iv/device_netlists/npn.spice b/models/xyce/testing/regression/bjt_iv/device_netlists/npn.spice
new file mode 100644
index 0000000..7bac45c
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_iv/device_netlists/npn.spice
@@ -0,0 +1,26 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 6
+Ib 0 b 9u
+
+xq1 c b 0 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+.DC Vcp 0 6 0.1 Ib 1u 9u 2u
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=npn/simulated_IcVc/{{i}}_simulated_{{device}}.csv {-I(Vcp)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_iv/device_netlists/pnp.spice b/models/xyce/testing/regression/bjt_iv/device_netlists/pnp.spice
new file mode 100644
index 0000000..76d695f
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_iv/device_netlists/pnp.spice
@@ -0,0 +1,26 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc -6
+Ib 0 b -9u
+
+xq1 c b 0 {{device}}
+
+
+*****************
+** Analysis
+*****************
+.DC Vcp 0 -3 -0.1 Ib -1u -9u -2u
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pnp/simulated_IcVc/{{i}}_simulated_{{device}}.csv {I(Vcp)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice b/models/xyce/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice
new file mode 100644
index 0000000..f76470a
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_iv/device_netlists/run_npn_vcic.spice
@@ -0,0 +1,26 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+
+Vcp c 0 dc 6
+Ib 0 b 9u
+
+xq1 c b 0 0 vnpn_10x10
+
+
+*****************
+** Analysis
+*****************
+.DC Vcp 0 6 0.1 Ib 1u 9u 2u
+.STEP TEMP 25 -60 200
+.print DC FORMAT=CSV file=result.csv {-I(Vcp)}
+
+.include "../../design.xyce"
+.lib "../../sm141064.xyce" bjt_typical
+
+.end
diff --git a/models/xyce/testing/regression/bjt_iv/models_regression.py b/models/xyce/testing/regression/bjt_iv/models_regression.py
new file mode 100644
index 0000000..5fe2163
--- /dev/null
+++ b/models/xyce/testing/regression/bjt_iv/models_regression.py
@@ -0,0 +1,208 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vc,step,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+
+ # Extracting measured values for each Device
+ for i in range (loops):
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ # Special case for 1st measured values
+ if i == 0 :
+ if device == "pnp":
+ temp_vc = vc
+ vc = "-vc "
+ # measured Id_sim
+ col_list = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv", index = False)
+ else:
+ if device == "pnp":
+ vc = temp_vc
+ # measured Id_sim
+ col_list = [f"{vc}",f"{ib}{step[0]}.{i}",f"{ib}{step[1]}.{i}",f"{ib}{step[2]}.{i}",f"{ib}{step[3]}.{i}",f"{ib}{step[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_measured.to_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv", index = False)
+
+def ext_simulated(device,vc,step,sweep,Id_sim,list_devices,ib):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ netlist_tmp = f"./device_netlists/{device}.spice"
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{Id_sim}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_{list_devices[k]}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = list_devices[k], i = i , temp = temp ))
+ netlist_path = f"{device}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_{list_devices[k]}.spice"
+
+ # Running Xyce for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",header=0)
+
+ # empty array to append in it shaped (sweep, number of trials + 1)
+ new_array = np.empty((sweep, 1+int(df_simulated.shape[0]/sweep)))
+ new_array[:, 0] = df_simulated.iloc[:sweep, 0]
+ times = int(df_simulated.shape[0]/sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*sweep:(j+1)*sweep, 0]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",index= False)
+ df_simulated.columns = [f"{vc}",f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]
+ df_simulated.to_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv",index= False)
+
+def error_cal(device,vc,step,Id_sim,list_devices,ib):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["corners"])
+ loops = dimensions["corners"].count()
+ temp_range = int(loops/4)
+ for i in range (loops):
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ elif i in range (2*temp_range,3*temp_range):temp = 125
+ else: temp = 175
+
+ k = i
+ if i >= len(list_devices):
+ while k >= len(list_devices):
+ k = k - len(list_devices)
+
+ measured = pd.read_csv(f"{device}/measured_{Id_sim}/{i}_measured_{list_devices[k]}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{Id_sim}/{i}_simulated_{list_devices[k]}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[0:, 1]) - abs(simulated.iloc[0:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[0:, 2]) - abs(simulated.iloc[0:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[0:, 3]) - abs(simulated.iloc[0:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[0:, 4]) - abs(simulated.iloc[0:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[0:, 5]) - abs(simulated.iloc[0:, 5]))/abs(measured.iloc[:, 5])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ df_error.to_csv(f"{device}/error_{Id_sim}/{i}_{device}_error_{list_devices[k]}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{ib}{step[0]}"].mean() + df_error[f"{ib}{step[1]}"].mean() + df_error[f"{ib}{step[2]}"].mean() +
+ df_error[f"{ib}{step[3]}"].mean() + df_error[f"{ib}{step[4]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"{ib}{step[0]}",f"{ib}{step[1]}",f"{ib}{step[2]}",f"{ib}{step[3]}",f"{ib}{step[4]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_ib = (df_error == max_error).idxmax(axis=1)[max_index]
+ if i == 0 :
+ if device == "pnp":
+ temp_vc = vc
+ vc = "-vc "
+ else:
+ if device == "pnp":
+ vc = temp_vc
+ max_location_vc = df_error[f"{vc}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'device': f'{list_devices[k]}','Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_ib} & Vc (V) = {max_location_vc}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["npn","pnp"]
+ list_devices = [["vnpn_10x10" , "vnpn_5x5" , "vnpn_0p54x16" , "vnpn_0p54x8" , "vnpn_0p54x4", "vnpn_0p54x2"],
+ ["vpnp_0p42x10" , "vpnp_0p42x5", "vpnp_10x10" , "vpnp_5x5"]]
+ vc = ["vcp ","-vc (A)"]
+ ib = ["ibp =","ib =-"]
+ Id_sim = "IcVc"
+ sweep = [61,31]
+ step = [ "1.000E-06" , "3.000E-06" , "5.000E-06" , "7.000E-06" , "9.000E-06"]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/BJT/bjt_{device}_icvc_f.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+
+ # =========== Simulate ==============
+ ext_measured (device,vc[i],step,Id_sim,list_devices[i],ib[i])
+
+ ext_simulated(device,vc[i],step,sweep[i],Id_sim,list_devices[i],ib[i])
+
+ # ============ Results =============
+ error_cal (device,vc[i],step,Id_sim,list_devices[i],ib[i])
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/diode/device_netlists/cv.spice b/models/xyce/testing/regression/diode/device_netlists/cv.spice
new file mode 100644
index 0000000..d5797e8
--- /dev/null
+++ b/models/xyce/testing/regression/diode/device_netlists/cv.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+.param volt = -3.3
+V1 in 0 dc {volt} ac 1
+R1 in out 1G
+dn1 out 0 np_3p3 AREA={10u*10u}
+
+.meas AC freq when Vdb(out)=-3
+*.meas AC C param = {1/(2*3.141592653589793238*freq*1000000000)}
+
+*****************
+** Analysis
+*****************
+*.control
+.ac dec 10 1 10G
+.step volt -12.13 1.13 0.13
+
+.include "../../design.xyce"
+.lib "../../sm141064.xyce" diode_typical
+
+.end
+
+
diff --git a/models/xyce/testing/regression/diode/device_netlists/iv.spice b/models/xyce/testing/regression/diode/device_netlists/iv.spice
new file mode 100644
index 0000000..2ed78b2
--- /dev/null
+++ b/models/xyce/testing/regression/diode/device_netlists/iv.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 dc 1
+
+
+dn1 n 0 {{device}} AREA={{area}}p
+* PJ=40u
+
+
+*****************
+** Analysis
+*****************
+.DC Vn -12.13 1.13 0.13
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file={{device}}_{{Id_sim}}_{{corner}}/simulated_{{Id_sim}}/{{i}}_simulated_A{{area}}_P{{pj}}.csv {abs(I(dn1))}
+* .print DC FORMAT=CSV file=result.csv {N(dn1:is)}
+
+
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" diode_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/diode/device_netlists/run_np_3p3_iv_independently.spice b/models/xyce/testing/regression/diode/device_netlists/run_np_3p3_iv_independently.spice
new file mode 100644
index 0000000..62580c1
--- /dev/null
+++ b/models/xyce/testing/regression/diode/device_netlists/run_np_3p3_iv_independently.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 dc 1
+
+
+dn1 n 0 sc_diode AREA={10u*10u}
+* PJ=40u
+
+
+*****************
+** Analysis
+*****************
+.DC Vn -12.13 1.13 0.13
+.STEP TEMP LIST -40 25 125 175
+.print DC FORMAT=CSV file=result.csv {abs(I(dn1))}
+* .print DC FORMAT=CSV file=result.csv {N(dn1:is)}
+
+
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" diode_typical
+
+.end
diff --git a/models/xyce/testing/regression/diode/models_regression.py b/models/xyce/testing/regression/diode/models_regression.py
new file mode 100644
index 0000000..983702e
--- /dev/null
+++ b/models/xyce/testing/regression/diode/models_regression.py
@@ -0,0 +1,187 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vn,d_in, Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[i]
+ length = dimensions["L (um)"].iloc[i]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vn}",f"{d_in}_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv", index = False)
+
+def ext_simulated(device,vn,d_in,vn_sweeps,Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ netlist_tmp = f"./device_netlists/{Id_sim}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+
+ if i % 4 == 0: temp = -40
+ elif i % 4 == 1: temp = 25
+ elif i % 4 == 2: temp = 125
+ else:
+ temp = 175
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{Id_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, area = width, pj = length, i = i , temp = temp, Id_sim = Id_sim , corner = corner ))
+ netlist_path = f"{dirpath}/{device}_netlists_{Id_sim}/{i}_{device}_netlist_A{width}_P{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",header=0)
+ #df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+
+ # zero array to append in it shaped (vn_sweeps, number of trials + 1)
+ new_array = np.zeros((vn_sweeps, 2))
+ new_array[:len(df_simulated.index), 0] = df_simulated.iloc[:, 0]
+
+ new_array[:len(df_simulated.index), 1] = df_simulated.iloc[:, 0]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+ df_simulated.columns = [f"{vn}",f"{d_in}_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i % 4 == 0: temp = -40
+ elif i % 4 == 1: temp = 25
+ elif i % 4 == 2: temp = 125
+ else:
+ temp = 175
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i}_measured_A{width}_P{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i}_simulated_A{width}_P{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i}_{device}_error_A{width}_P{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"{d_in}_{corner}"].mean())/6
+ # Max error
+ max_error = df_error[f"{d_in}_{corner}"].max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vn}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{dirpath}', 'Area': f'{width}', 'Perimeter': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vn (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["np_3p3","dnwpw","dnwps","np_6p0", "nwp_3p3","nwp_6p0","pn_3p3","pn_6p0","sc_diode"]
+ corners = ["typical","ff","ss"]
+ measures = [["iv","Vn1 (V)", " |In1(A)| diode", 103]]#,
+ # ["cv","Vj", "diode", 17]]
+
+ for device in devices:
+ for measure in measures:
+ for corner in corners:
+ # Folder structure of measured values
+ Id_sim, diode_vn, diode_in, no_of_vn_sweeps = measure[0], measure[1], measure[2], measure[3]
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{Id_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Diode/{device}_{Id_sim}.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{Id_sim}",exist_ok=False)
+
+ ext_measured (device,diode_vn,diode_in, Id_sim, corner)
+
+ ext_simulated(device,diode_vn,diode_in,no_of_vn_sweeps,Id_sim, corner)
+
+ for device in devices:
+ for measure in measures:
+ for corner in corners:
+ # Folder structure of measured values
+ Id_sim, diode_vn, diode_in, no_of_vn_sweeps = measure[0], measure[1], measure[2], measure[3]
+ error_cal (device,diode_vn,diode_in,Id_sim, corner)
+
+
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/mimcap_c/device_netlists/mimcap.spice b/models/xyce/testing/regression/mimcap_c/device_netlists/mimcap.spice
new file mode 100644
index 0000000..491a56f
--- /dev/null
+++ b/models/xyce/testing/regression/mimcap_c/device_netlists/mimcap.spice
@@ -0,0 +1,30 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+.param volt = -3.0
+V1 in 0 dc {volt} ac 1
+R1 in out 1G
+xcn out 0 {{device}} c_length={{length}}u c_width={{width}}u l={{length}}u w={{width}}u
+
+.meas AC freq when Vdb(out)=-3 PRECISION=15
+
+
+
+
+*****************
+** Analysis
+*****************
+
+.ac dec 10 1 10G
+.step volt {{voltage}}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" mimcap_{{corner}}
+
+.end
+
+
diff --git a/models/xyce/testing/regression/mimcap_c/models_regression.py b/models/xyce/testing/regression/mimcap_c/models_regression.py
new file mode 100644
index 0000000..625e5ac
--- /dev/null
+++ b/models/xyce/testing/regression/mimcap_c/models_regression.py
@@ -0,0 +1,181 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ if i == 0 :
+ # measured cv
+ col_list = [f"Vj",f"mimcap_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+ else:
+ # measured cv
+ col_list = [f"Vj",f"mimcap_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"Vj",f"mimcap_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,start,voltage):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ netlist_tmp = f"./device_netlists/mimcap.spice"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner , voltage = voltage))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = []
+ # Writing simulated data
+ for j in range(len([x for x in os.listdir(f"{dirpath}/{device}_netlists_{cv_sim}") if f"{i-start}_{device}_netlist_w{width}_l{length}.spice.ma" in x])):
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice.ma{j}") as f:
+ cap = 1000000/(float(next(f).replace("FREQ = ", ""))*2*np.pi)
+ df_simulated.append(cap)
+
+ # zero array to append in it shaped (vn_sweeps, number of trials + 1)
+ new_array = np.zeros((len(df_simulated), 2))
+ new_array[:len(df_simulated), 0] = df_simulated
+ new_array[:len(df_simulated), 1] = df_simulated
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.columns = [f"Vj",f"mimcap_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i-start}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ df_final = pd.DataFrame()
+ for i in range (start,4+start):
+ if i == 0+start: width = 100 ; length = 100
+ if i == 1+start: width = 5 ; length = 5
+ if i == 2+start: width = 100 ; length = 5
+ if i == 3+start: width = 5 ; length = 100
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i-start}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i-start}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i-start}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"mimcap_{corner}"].mean())
+ # Max error
+ max_error = df_error[f"mimcap_{corner}"].max()
+
+ df_final_ = {'Run no.': f'{i-start}', 'Device name': f'{dirpath}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+
+def main():
+
+ # 3p3
+ corners = ["ss" , "typical","ff"]
+ devices = ["mim_1p5fF" , "mim_1p0fF" , "mim_2p0fF"]
+ measure = ["cv","corners", "CV (fF)"]
+ voltage = ["-3.0 3.0 0.1"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/mimcap_fc.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start,voltage[0])
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice
new file mode 100644
index 0000000..87c6de8
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/nmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** nmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=0
+vgs G_tn 0 dc=3.3
+vbs S_tn 0 dc=0
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = {{width}}u L = {{length}}u nf={{nf}} ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vgs_min = -3.3
+let vgs_step = 0.1
+let vgs_max = 3.3
+
+compose vbs_vector start=0 stop=-3.3 step=-0.825
+
+set appendwrite
+
+foreach t 25
+
+ let vbs_counter = 0
+ while vbs_counter < length(vbs_vector)
+ option TEMP=25
+ alter vbs = vbs_vector[vbs_counter]
+
+ save @mn[vs] @mn[vgs] @mn[id] @mn[cgb]
+ *******************
+ ** simulation part
+ *******************
+ DC vgs $&vgs_min $&vgs_max $&vgs_step
+
+ * ** parameters calculation
+
+ print @mn[cgb]
+
+ wrdata nmos_3p3_cv/simulated_Cgc/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgb]
+
+ reset
+ let vbs_counter = vbs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice
new file mode 100644
index 0000000..377a4b1
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgc/pmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** pmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=0
+vgs G_tn 0 dc=3.3
+vbs S_tn 0 dc=0
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = {{width}}u L = {{length}}u nf={{nf}} ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vgs_min = -3.3
+let vgs_step = 0.1
+let vgs_max = 3.3
+
+compose vbs_vector start=0 stop=3.3 step=0.825
+
+set appendwrite
+
+foreach t 25
+
+ let vbs_counter = 0
+ while vbs_counter < length(vbs_vector)
+ option TEMP=25
+ alter vbs = vbs_vector[vbs_counter]
+
+ save @mn[vs] @mn[vgs] @mn[id] @mn[cgb]
+ *******************
+ ** simulation part
+ *******************
+ DC vgs $&vgs_min $&vgs_max $&vgs_step
+
+ * ** parameters calculation
+
+ print @mn[cgb]
+
+ wrdata pmos_3p3_cv/simulated_Cgc/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgb]
+
+ reset
+ let vbs_counter = vbs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice
new file mode 100644
index 0000000..f85c481
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/nmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** nmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+vs S_tn 0 dc=0.2
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.1
+let vds_max = 3.3
+
+compose vgs_vector start=0 stop=3.3 step=1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgd]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgd]
+
+ wrdata nmos_3p3_cv/simulated_Cgd/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgd]
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice
new file mode 100644
index 0000000..4e35ebc
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgd/pmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** pmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+vs S_tn 0 dc=0.2
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.1
+let vds_max = -3.3
+
+compose vgs_vector start=0 stop=-3.3 step=-1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgd]
+
+ wrdata pmos_3p3_cv/simulated_Cgd/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgd]
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice
new file mode 100644
index 0000000..9358ece
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/nmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** nmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=3.3
+vgs G_tn 0 dc=3.3
+vs S_tn 0 dc=0.1
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn nmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = 0.1
+let vds_max = 3.3
+
+compose vgs_vector start=0 stop=3.3 step=1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgs]
+
+ wrdata nmos_3p3_cv/simulated_Cgs/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgs]
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice b/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice
new file mode 100644
index 0000000..c01b53e
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/device_netlists_Cgs/pmos_3p3_cv.spice
@@ -0,0 +1,62 @@
+***************************
+** pmos_3p3_cv
+***************************
+
+** library calling
+
+.include "../../180MCU_SPICE_hspice/design.xyce"
+.lib "../../180MCU_SPICE_hspice/sm141064.xyce" typical
+
+
+** Circuit Description **
+* power supply
+vds D_tn 0 dc=-3.3
+vgs G_tn 0 dc=-3.3
+vs S_tn 0 dc=0.1
+
+.temp 25
+.options tnom=25
+
+*l_diff_min = 0.24
+* ad = int((nf+1)/2) * width/nf * 0.24 = 24u
+* pd = (int((nf+1)/2) * width/nf + 0.24)*2 = 200.48u
+
+* circuit
+mn D_tn G_tn S_tn S_tn pmos_3p3 W = 200u L = 0.28u nf=20 ad= 24u pd=200.48u as=24u ps=200.48u
+
+.control
+set filetype=ascii
+
+let vds_min = 0
+let vds_step = -0.1
+let vds_max = -3.3
+
+compose vgs_vector start=0 stop=-3.3 step=-1
+
+set appendwrite
+
+foreach t 25
+
+ let vgs_counter = 0
+ while vgs_counter < length(vgs_vector)
+ option TEMP=25
+ alter vgs = vgs_vector[vgs_counter]
+
+ save @mn[vds] @mn[vgs] @mn[id] @mn[cgs]
+ *******************
+ ** simulation part
+ *******************
+ DC vds $&vds_min $&vds_max $&vds_step
+
+ * ** parameters calculation
+
+ print @mn[cgs]
+
+ wrdata pmos_3p3_cv/simulated_Cgs/{{i}}_simulated_W{{width}}_L{{length}}.csv @mn[cgs]
+
+ reset
+ let vgs_counter = vgs_counter + 1
+ end
+end
+.endc
+.end
diff --git a/models/xyce/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx b/models/xyce/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx
new file mode 100644
index 0000000..813635c
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/measured_data/3p3_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx b/models/xyce/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..fd139e1
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/measured_data/3p3_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx b/models/xyce/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx
new file mode 100644
index 0000000..a6d80ee
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/measured_data/6p0_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx b/models/xyce/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx
new file mode 100644
index 0000000..407129c
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/measured_data/6p0_nat_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx b/models/xyce/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx
new file mode 100644
index 0000000..1dbd85c
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/measured_data/6p0_sab_cv.nl_out.xlsx
Binary files differ
diff --git a/models/xyce/testing/regression/mos_cv/models_regression.py b/models/xyce/testing/regression/mos_cv/models_regression.py
new file mode 100644
index 0000000..d31986d
--- /dev/null
+++ b/models/xyce/testing/regression/mos_cv/models_regression.py
@@ -0,0 +1,280 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,sweep_x,sweep_y,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Cgc
+ if sim_val == "Cgc":
+ col_list = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ else:
+ # measured Cgs & Cgd
+ if sim_val == "Cgs":
+ col_list = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ else:
+ col_list = [sweep_x,f"{sweep_y[0]}.1",f"{sweep_y[1]}.1",f"{sweep_y[2]}.1",f"{sweep_y[3]}.1"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ df_measured.to_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Cgc
+ if sim_val == "Cgc":
+ col_list = [sweep_x,f"{sweep_y[0]}.{i}",f"{sweep_y[1]}.{i}",f"{sweep_y[2]}.{i}",f"{sweep_y[3]}.{i}",f"{sweep_y[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,f"{sweep_y[0]}",f"{sweep_y[1]}",f"{sweep_y[2]}",f"{sweep_y[3]}",f"{sweep_y[4]}"]
+ else:
+ # measured Cgs & Cgd
+ cgs_index = 2*i
+ cgd_index = cgs_index + 1
+ if sim_val == "Cgs":
+ col_list = [sweep_x,f"{sweep_y[0]}.{cgs_index}",f"{sweep_y[1]}.{cgs_index}",f"{sweep_y[2]}.{cgs_index}",f"{sweep_y[3]}.{cgs_index}"]
+ else:
+ col_list = [sweep_x,f"{sweep_y[0]}.{cgd_index}",f"{sweep_y[1]}.{cgd_index}",f"{sweep_y[2]}.{cgd_index}",f"{sweep_y[3]}.{cgd_index}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [sweep_x,f"{sweep_y[0]}",f"{sweep_y[1]}",f"{sweep_y[2]}",f"{sweep_y[3]}"]
+
+ df_measured.to_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv", index = False)
+
+def ext_simulated(device,sweep_x,sweep_y,vds_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i == 0:
+ nf = 20
+ else:
+ nf = 1
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i, nf = nf ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+ # empty array to append in it shaped (vds_sweep, number of trials + 1)
+ new_array = np.empty((vds_sweep, 1+int(df_simulated.shape[0]/vds_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vds_sweep, 0]
+ times = int(df_simulated.shape[0]/vds_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vds_sweep:(j+1)*vds_sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ if sim_val == "Cgc":
+ df_simulated.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]
+ else:
+ df_simulated.columns = [sweep_x,sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,sweep_x,sweep_y,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = int(dimensions["L (um)"].count()/2)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * (abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1]),6)
+ error_2 = round (100 * (abs(measured.iloc[:, 2]) - abs(simulated.iloc[:, 2]))/abs(measured.iloc[:, 2]),6)
+ error_3 = round (100 * (abs(measured.iloc[:, 3]) - abs(simulated.iloc[:, 3]))/abs(measured.iloc[:, 3]),6)
+ error_4 = round (100 * (abs(measured.iloc[:, 4]) - abs(simulated.iloc[:, 4]))/abs(measured.iloc[:, 4]),6)
+ if sim_val == "Cgc":
+ error_5 = round (100 * (abs(measured.iloc[:, 5]) - abs(simulated.iloc[:, 5]))/abs(measured.iloc[:, 5]),6)
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ else:
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ if sim_val == "Cgc":
+ mean_error = (df_error[sweep_y[0]].mean() + df_error[sweep_y[1]].mean() + df_error[sweep_y[2]].mean() +
+ df_error[sweep_y[3]].mean() + df_error[sweep_y[4]].mean())/5
+ else:
+ mean_error = (df_error[sweep_y[0]].mean() + df_error[sweep_y[1]].mean() + df_error[sweep_y[2]].mean() +
+ df_error[sweep_y[3]].mean())/4
+ # Max error
+ if sim_val == "Cgc":
+ max_error = df_error[[sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3],sweep_y[4]]].max().max()
+ else:
+ max_error = df_error[[sweep_y[0],sweep_y[1],sweep_y[2],sweep_y[3]]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_sweep_y = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_sweep_x = df_error[sweep_x][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'25', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_sweep_y} & {sweep_x}= {max_location_sweep_x}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_cv" , "pmos_3p3_cv" ]
+ measured_data = ["3p3_cv"]
+ nmos_vds = "Vds (V)"
+ pmos_vds = "-Vds (V)"
+ nmos_vgs = "Vgs (V)"
+ pmos_vgs = "-Vgs (V)"
+ nmos_rds = "Rds"
+ cgc_sim = "Cgc"
+ cgs_sim = "Cgs"
+ cgd_sim = "Cgd"
+ Rds_sim = "Rds"
+ mos_3p3_vbs_sweep = 67
+ mos_3p3_vgs_sweep = 34
+
+ mos_6p0_vgs_sweep = 133
+
+ nmos3p3_vgs = ["Vgs=0" , "Vgs=1.1" , "Vgs=2.2" , "Vgs=3.3" ]
+ pmos3p3_vgs = ["Vgs=-0" , "Vgs=-1.1" , "Vgs=-2.2" , "Vgs=-3.3"]
+ # pmos3p3_vgs = [-0.8 , -1.3 , -1.8 , -2.3 , -2.8 , -3.3]
+ # nmos6p0_vgs = [ 1 , 2 , 3 , 4 , 5 , 6]
+ # pmos6p0_vgs = [-1 , -2 , -3 , -4 , -5 , -6]
+ # nmos6p0_nat_vgs = [ 0.25 , 1.4 , 2.55 , 3.7 , 4.85 , 6]
+
+ nmos3p3_vbs = ["Vbs=0" , "Vbs=-0.825" , "Vbs=-1.65" , "Vbs=-2.475" , "Vbs=-3.3"]
+ pmos3p3_vbs = ["Vbs=-0" , "Vbs=0.825" , "Vbs=1.65" , "Vbs=2.475" , "Vbs=3.3" ]
+ # nmos6p0_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+ # pmos6p0_vbs = [ 0 , 0.75 , 1.5 , 2.25 , 3]
+ # nmos6p0_nat_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{cgd_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"./measured_data/{measured_data[0]}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{cgd_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgc_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgs_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{cgd_sim}",exist_ok=False)
+
+
+ # =========== nmos_3p3_cv ==============
+ # Cgc
+ ext_measured ("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,cgc_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,cgc_sim)
+ error_cal ("nmos_3p3_cv",nmos_vgs,nmos3p3_vbs,cgc_sim)
+
+ # Cgs
+ ext_measured ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgs_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,cgs_sim)
+ error_cal ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgs_sim)
+
+ # Cgd
+ ext_measured ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgd_sim)
+ ext_simulated("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,cgd_sim)
+ error_cal ("nmos_3p3_cv",nmos_vds,nmos3p3_vgs,cgd_sim)
+
+
+ # =========== nmos_3p3_cv ==============
+ # Cgc
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,cgc_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,mos_3p3_vbs_sweep,cgc_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vbs,cgc_sim)
+
+ # Cgs
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgs_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,mos_3p3_vgs_sweep,cgs_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgs_sim)
+
+ # Cgd
+ ext_measured ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgd_sim)
+ ext_simulated("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,mos_3p3_vgs_sweep,cgd_sim)
+ error_cal ("pmos_3p3_cv",pmos_vgs,pmos3p3_vgs,cgd_sim)
+
+ # =========== pmos_3p3_iv ==============
+
+
+ # =========== nmos_6p0_iv ==============
+
+
+ # =========== pmos_6p0_iv ==============
+
+
+ # ============ nmos_3p3_sab_iv ============= # Error in ngspice
+
+
+ # ============ nmos_6p0_nat_iv =============
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv_vbs.spice b/models/xyce/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv_vbs.spice
new file mode 100644
index 0000000..0d09520
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vbs/device_netlists_Id/nmos_3p3_iv_vbs.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 0.05
+Vgs G_tn 0 dc 3.3
+Vbs S_tn 0 dc 0
+
+xmn1 D_tn G_tn 0 S_tn nmos_3p3 W=10u L=10u ad=2.4u pd=20.48u as=2.4u ps=20.48u
+
+
+*****************
+** Analysis
+*****************
+.dc Vgs 0 3.3 0.05 Vbs 0 -3.3 -0.825
+.STEP TEMP -40 -60 200
+.print DC FORMAT=CSV file=result.csv {-I(Vds)}
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" typical
+
+.end
diff --git a/models/xyce/testing/regression/mos_iv_vbs/models_regression.py b/models/xyce/testing/regression/mos_iv_vbs/models_regression.py
new file mode 100644
index 0000000..3a85b3e
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vbs/models_regression.py
@@ -0,0 +1,224 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"ngspice -b -a {file_name} -o {file_name}.log > {file_name}.log")
+
+def ext_measured(device,vgs,vbs):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops*2,2):
+ width = dimensions["W (um)"].iloc[int(i/2)]
+ length = dimensions["L (um)"].iloc[int(i/2)]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ if device in ["pmos_3p3_iv" , "pmos_6p0_iv"]:
+ col_list = ['-vgs ',f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ else:
+ col_list = ['vgs ',f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vgs}",f"vbs ={vbs[0]}.{i}",f"vbs ={vbs[1]}.{i}",f"vbs ={vbs[2]}.{i}",f"vbs ={vbs[3]}.{i}",f"vbs ={vbs[4]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+
+
+def ext_simulated(device,vgs,vbs,vbs_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ AD = float(width) * 0.24
+ PD = 2 * (float(width) + 0.24)
+ AS = AD
+ PS = PD
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else:
+ temp = 125
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i , temp = temp , AD = AD , PD = PD , AS = AS , PS = PD ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=None, delimiter=r"\s+")
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+ # empty array to append in it shaped (vbs_sweep, number of trials + 1)
+ new_array = np.empty((vbs_sweep, 1+int(df_simulated.shape[0]/vbs_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vbs_sweep, 0]
+ times = int(df_simulated.shape[0]/vbs_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vbs_sweep:(j+1)*vbs_sweep, 1]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ df_simulated.columns = [f"{vgs}",f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,vgs,vbs,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else: temp = 125
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[:, 2]) - abs(simulated.iloc[:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[:, 3]) - abs(simulated.iloc[:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[:, 4]) - abs(simulated.iloc[:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[:, 5]) - abs(simulated.iloc[:, 5]))/abs(measured.iloc[:, 5])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"vbs ={vbs[0]}"].mean() + df_error[f"vbs ={vbs[1]}"].mean() + df_error[f"vbs ={vbs[2]}"].mean() +
+ df_error[f"vbs ={vbs[3]}"].mean() + df_error[f"vbs ={vbs[4]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"vbs ={vbs[0]}",f"vbs ={vbs[1]}",f"vbs ={vbs[2]}",f"vbs ={vbs[3]}",f"vbs ={vbs[4]}"]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vbs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vgs = df_error[f"{vgs}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & vbs (V) = {max_location_vbs}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_iv" , "pmos_3p3_iv" , "nmos_6p0_iv" , "pmos_6p0_iv" , "nmos_6p0_nat_iv"] #"nmos_3p3_sab_iv"
+ nmos_vgs = "vgs (V)"
+ pmos_vgs = "-vgs (V)"
+ nmos_rds = "Rds"
+ Id_sim = "Id"
+ Rds_sim = "Rds"
+ mos_3p3_vbs_sweep = 67
+ mos_6p0_vbs_sweep = 121
+ mos_6p0_nat_vbs_sweep = 131
+ nmos3p3_vbs = [0 , -0.825 , -1.65 , -2.48 , -3.3]
+ pmos3p3_vbs = [0 , 0.825 , 1.65 , 2.48 , 3.3]
+ nmos6p0_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+ pmos6p0_vbs = [ 0 , 0.75 , 1.5 , 2.25 , 3]
+ nmos6p0_nat_vbs = [ 0 , -0.75 , -1.5 , -2.25 , -3]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/measured_{Rds_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/MOS/{device}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/simulated_{Rds_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+ # os.makedirs(f"{device}/error_{Rds_sim}",exist_ok=False)
+
+ # =========== nmos_3p3_iv ==============
+ ext_measured ("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs)
+ ext_simulated("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ error_cal ("nmos_3p3_iv",nmos_vgs,nmos3p3_vbs,Id_sim)
+
+ # =========== pmos_3p3_iv ==============
+ ext_measured ("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs)
+ ext_simulated("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ error_cal ("pmos_3p3_iv",pmos_vgs,pmos3p3_vbs,Id_sim)
+
+ # =========== nmos_6p0_iv ==============
+ ext_measured ("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs)
+ ext_simulated("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs,mos_6p0_vbs_sweep,Id_sim)
+ error_cal ("nmos_6p0_iv",nmos_vgs,nmos6p0_vbs,Id_sim)
+
+ # =========== pmos_6p0_iv ==============
+ ext_measured ("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs)
+ ext_simulated("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs,mos_6p0_vbs_sweep,Id_sim)
+ error_cal ("pmos_6p0_iv",pmos_vgs,pmos6p0_vbs,Id_sim)
+
+ # ============ nmos_3p3_sab_iv ============= # Error in ngspice
+ # ext_measured ("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs)
+ # ext_simulated("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs,mos_3p3_vbs_sweep,Id_sim)
+ # error_cal ("nmos_3p3_sab_iv",nmos_vgs,nmos3p3_vbs,Rds_sim)
+
+ # ============ nmos_6p0_nat_iv =============
+ ext_measured ("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs)
+ ext_simulated("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs,mos_6p0_nat_vbs_sweep,Id_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vgs,nmos6p0_nat_vbs,Id_sim)
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice
new file mode 100644
index 0000000..4e307ff
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..4a01057
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_3p3_sab_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_3p3_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice
new file mode 100644
index 0000000..889d85b
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 1 6 1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice
new file mode 100644
index 0000000..688a445
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_nat_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_nat W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 0.25 6 1.15
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_nat_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..ba9ec40
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/nmos_6p0_sab_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 1 6 1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice
new file mode 100644
index 0000000..65ec777
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_3p3 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_3p3_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..8c95a4e
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_3p3_sab_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_3p3_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_3p3_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice
new file mode 100644
index 0000000..4cb584c
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_6p0 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_6p0_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..eb914ad
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/pmos_6p0_sab_iv.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_6p0_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_6p0_sab_iv/simulated_Id/{{i}}_simulated_W{{width}}_L{{length}}.csv {-I(Vds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/run_nmos_3p3_iv_independently.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/run_nmos_3p3_iv_independently.spice
new file mode 100644
index 0000000..993faeb
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Id/run_nmos_3p3_iv_independently.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+* .OPTIONS DEVICE TEMP=-40
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3_sab W=10u L=10u ad=2.4u pd=20.48u as=2.4u ps=20.48u
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP 25 -60 200
+.print DC FORMAT=CSV file=result.csv {-I(Vds)}
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice
new file mode 100644
index 0000000..ccc637a
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_3p3_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {1/N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..009c02c
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_3p3_sab_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_3p3_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {1/N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice
new file mode 100644
index 0000000..5b4fad8
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 1 6 1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice
new file mode 100644
index 0000000..a83c949
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_nat_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_nat W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 0.25 6 1.15
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_nat_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {1/N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..de6c2b4
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/nmos_6p0_sab_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_6p0_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 6.6 0.05 Vgs 1 6 1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=nmos_6p0_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice
new file mode 100644
index 0000000..3104bbe
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_3p3 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_3p3_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {1/N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice
new file mode 100644
index 0000000..15a279a
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_3p3_sab_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_3p3_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -3.3 -0.05 Vgs -0.8 -3.3 -0.5
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_3p3_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {1/N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice
new file mode 100644
index 0000000..08035f6
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_6p0 W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_6p0_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice
new file mode 100644
index 0000000..d1bea5b
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/pmos_6p0_sab_iv.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc -3.3
+Vgs G_tn 0 dc -3.3
+
+
+xmn1 D_tn G_tn 0 0 pmos_6p0_sab W={{width}}u L={{length}}u ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 -6.6 -0.05 Vgs -1 -6 -1
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file=pmos_6p0_sab_iv/simulated_Rds/{{i}}_simulated_W{{width}}_L{{length}}.csv {N(xmn1:m0:gds)}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/run_nmos_3p3_rds_independently.spice b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/run_nmos_3p3_rds_independently.spice
new file mode 100644
index 0000000..4b90836
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/device_netlists_Rds/run_nmos_3p3_rds_independently.spice
@@ -0,0 +1,27 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+
+*****************
+** main netlist
+*****************
+Vds D_tn 0 dc 3.3
+Vgs G_tn 0 dc 3.3
+
+
+xmn1 D_tn G_tn 0 0 nmos_3p3 W=10u L=10u ad=2.4u pd=20.48u as=2.4u ps=20.48u
+
+
+
+*****************
+** Analysis
+*****************
+.DC Vds 0 3.3 0.05 Vgs 0.8 3.3 0.5
+.STEP TEMP -40 -40 -40
+.print DC FORMAT=CSV file=result.csv {1/N(xmn1:m0:gds)}
+
+.include "../../design.xyce"
+.lib "../../sm141064.xyce" typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/mos_iv_vgs/models_regression.py b/models/xyce/testing/regression/mos_iv_vgs/models_regression.py
new file mode 100644
index 0000000..e59763d
--- /dev/null
+++ b/models/xyce/testing/regression/mos_iv_vgs/models_regression.py
@@ -0,0 +1,287 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce {file_name} -l {file_name}.log")
+
+def ext_measured(device,vds,vgs):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops*2,2):
+ width = dimensions["W (um)"].iloc[int(i/2)]
+ length = dimensions["L (um)"].iloc[int(i/2)]
+
+ # Special case for 1st measured values
+ if i == 0 :
+ # measured Id
+ col_list = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ # measured Rds
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i+1}",f"vgs ={vgs[1]}.{i+1}",f"vgs ={vgs[2]}.{i+1}",f"vgs ={vgs[3]}.{i+1}",f"vgs ={vgs[4]}.{i+1}",f"vgs ={vgs[5]}.{i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Rds/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ else:
+ # measured Id
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i}",f"vgs ={vgs[1]}.{i}",f"vgs ={vgs[2]}.{i}",f"vgs ={vgs[3]}.{i}",f"vgs ={vgs[4]}.{i}",f"vgs ={vgs[5]}.{i}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Id/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+ # measured Rds
+ col_list = [f"{vds}",f"vgs ={vgs[0]}.{i+1}",f"vgs ={vgs[1]}.{i+1}",f"vgs ={vgs[2]}.{i+1}",f"vgs ={vgs[3]}.{i+1}",f"vgs ={vgs[4]}.{i+1}",f"vgs ={vgs[5]}.{i+1}"]
+ df_measured = pd.read_csv(f"{device}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_measured.to_csv(f"{device}/measured_Rds/{int(i/2)}_measured_W{width}_L{length}.csv", index = False)
+
+def ext_simulated(device,vds,vgs,vds_sweep,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ netlist_tmp = f"./device_netlists_{sim_val}/{device}.spice"
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ AD = float(width) * 0.24
+ PD = 2 * (float(width) + 0.24)
+ AS = AD
+ PS = PD
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else:
+ temp = 125
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{device}/{device}_netlists_{sim_val}",exist_ok=True)
+ with open(f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(width = width,length = length,i = i , temp = temp , AD = AD , PD = PD , AS = AS , PS = PD ))
+ netlist_path = f"{device}/{device}_netlists_{sim_val}/{i}_{device}_netlist_W{width}_L{length}.spice"
+ if ("sab" not in netlist_path) and ("Rds" not in netlist_path):
+ netlist_path = "-hspice-ext all " + netlist_path
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",header=0)
+
+ # empty array to append in it shaped (vds_sweep, number of trials + 1)
+ new_array = np.empty((vds_sweep, 1+int(df_simulated.shape[0]/vds_sweep)))
+ new_array[:, 0] = df_simulated.iloc[:vds_sweep, 0]
+ times = int(df_simulated.shape[0]/vds_sweep)
+
+ for j in range(times):
+ new_array[:, (j+1)] = df_simulated.iloc[j*vds_sweep:(j+1)*vds_sweep, 0]
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+ df_simulated.columns = [f"{vds}",f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}"]
+ df_simulated.to_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv",index= False)
+
+def error_cal(device,vds,vgs,sim_val):
+
+ # Get dimensions used for each device
+ dimensions = pd.read_csv(f"{device}/{device}.csv",usecols=["W (um)" , "L (um)"])
+ loops = dimensions["L (um)"].count()
+ temp_range = int(loops/3)
+ df_final = pd.DataFrame()
+ for i in range (0,loops):
+ width = dimensions["W (um)"].iloc[int(i)]
+ length = dimensions["L (um)"].iloc[int(i)]
+ if i in range (0,temp_range): temp = 25
+ elif i in range (temp_range,2*temp_range): temp = -40
+ else: temp = 125
+
+ measured = pd.read_csv(f"{device}/measured_{sim_val}/{i}_measured_W{width}_L{length}.csv")
+ simulated = pd.read_csv(f"{device}/simulated_{sim_val}/{i}_simulated_W{width}_L{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[1:, 1]) - abs(simulated.iloc[1:, 1]))/abs(measured.iloc[:, 1])),6)
+ error_2 = round (100 * abs((abs(measured.iloc[1:, 2]) - abs(simulated.iloc[1:, 2]))/abs(measured.iloc[:, 2])),6)
+ error_3 = round (100 * abs((abs(measured.iloc[1:, 3]) - abs(simulated.iloc[1:, 3]))/abs(measured.iloc[:, 3])),6)
+ error_4 = round (100 * abs((abs(measured.iloc[1:, 4]) - abs(simulated.iloc[1:, 4]))/abs(measured.iloc[:, 4])),6)
+ error_5 = round (100 * abs((abs(measured.iloc[1:, 5]) - abs(simulated.iloc[1:, 5]))/abs(measured.iloc[:, 5])),6)
+ error_6 = round (100 * abs((abs(measured.iloc[1:, 6]) - abs(simulated.iloc[1:, 6]))/abs(measured.iloc[:, 6])),6)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1,error_2,error_3,error_4,error_5,error_6]).transpose()
+ df_error.to_csv(f"{device}/error_{sim_val}/{i}_{device}_error_W{width}_L{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"vgs ={vgs[0]}"].mean() + df_error[f"vgs ={vgs[1]}"].mean() + df_error[f"vgs ={vgs[2]}"].mean() +
+ df_error[f"vgs ={vgs[3]}"].mean() + df_error[f"vgs ={vgs[4]}"].mean() + df_error[f"vgs ={vgs[5]}"].mean())/6
+ # Max error
+ max_error = df_error[[f"vgs ={vgs[0]}",f"vgs ={vgs[1]}",f"vgs ={vgs[2]}",f"vgs ={vgs[3]}",f"vgs ={vgs[4]}",f"vgs ={vgs[5]}" ]].max().max()
+ # Max error location
+ max_index = max((df_error == max_error).idxmax())
+ max_location_vgs = (df_error == max_error).idxmax(axis=1)[max_index]
+ max_location_vds = df_error[f"{vds}"][max_index]
+
+ df_final_ = {'Run no.': f'{i}', 'Temp': f'{temp}', 'Device name': f'{device}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{sim_val}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} @ {max_location_vgs} & Vds (V) = {max_location_vds}'}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{device}/Final_report_{sim_val}.csv", index = False)
+ out_report = pd.read_csv (f"{device}/Final_report_{sim_val}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ devices = ["nmos_3p3_iv" , "pmos_3p3_iv" , "nmos_6p0_iv" , "pmos_6p0_iv" , "nmos_6p0_nat_iv", "nmos_3p3_sab_iv", "pmos_3p3_sab_iv" , "nmos_6p0_sab_iv" , "pmos_6p0_sab_iv"]
+ nmos_vds = "vds (V)"
+ pmos_vds = "-vds (V)"
+ nmos_rds = "Rds"
+ Id_sim = "Id"
+ Rds_sim = "Rds"
+ mos_3p3_vgs_sweep = 67
+ mos_6p0_vgs_sweep = 133
+ nmos3p3_vgs = [ 0.8 , 1.3 , 1.8 , 2.3 , 2.8 , 3.3]
+ pmos3p3_vgs = [-0.8 , -1.3 , -1.8 , -2.3 , -2.8 , -3.3]
+ nmos6p0_vgs = [ 1 , 2 , 3 , 4 , 5 , 6]
+ pmos6p0_vgs = [-1 , -2 , -3 , -4 , -5 , -6]
+ nmos6p0_nat_vgs = [ 0.25 , 1.4 , 2.55 , 3.7 , 4.85 , 6]
+
+ for device in devices:
+ # Folder structure of measured values
+ dirpath = f"{device}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{device}/measured_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/measured_{Rds_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/MOS/{device}.nl_out.xlsx")
+ read_file.to_csv (f"{device}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/simulated_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/simulated_{Rds_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Id_sim}",exist_ok=False)
+ os.makedirs(f"{device}/error_{Rds_sim}",exist_ok=False)
+
+ # =========== nmos_3p3_iv ==============
+ ext_measured ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs)
+
+ ext_simulated("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+
+ ext_simulated("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+
+ # =========== pmos_3p3_iv ==============
+ ext_measured ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs)
+
+ ext_simulated("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+
+ ext_simulated("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+
+ # =========== nmos_6p0_iv ==============
+ ext_measured ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs)
+
+ ext_simulated("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+
+ ext_simulated("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+
+ # =========== pmos_6p0_iv ==============
+ ext_measured ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs)
+
+ ext_simulated("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+
+ ext_simulated("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+
+ # ============ nmos_6p0_nat_iv =============
+ ext_measured ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs)
+
+ ext_simulated("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,mos_6p0_vgs_sweep,Id_sim)
+
+ ext_simulated("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,mos_6p0_vgs_sweep,Rds_sim)
+
+ # ============ nmos_3p3_sab_iv =============
+ ext_measured ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs)
+
+ ext_simulated("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+
+ ext_simulated("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+
+ # =========== pmos_3p3_sab_iv ==============
+ ext_measured ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs)
+
+ ext_simulated("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Id_sim)
+
+ ext_simulated("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,mos_3p3_vgs_sweep,Rds_sim)
+
+ # =========== nmos_6p0_sab_iv ==============
+ ext_measured ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs)
+
+ ext_simulated("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+
+ ext_simulated("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+
+ # =========== pmos_6p0_sab_iv ==============
+ ext_measured ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs)
+
+ ext_simulated("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Id_sim)
+
+ ext_simulated("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,mos_6p0_vgs_sweep,Rds_sim)
+
+
+
+ # ============ Results =============
+ error_cal ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,Id_sim)
+ error_cal ("nmos_3p3_iv",nmos_vds,nmos3p3_vgs,Rds_sim)
+ error_cal ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,Id_sim)
+ error_cal ("pmos_3p3_iv",pmos_vds,pmos3p3_vgs,Rds_sim)
+ error_cal ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,Id_sim)
+ error_cal ("nmos_6p0_iv",nmos_vds,nmos6p0_vgs,Rds_sim)
+ error_cal ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,Id_sim)
+ error_cal ("pmos_6p0_iv",pmos_vds,pmos6p0_vgs,Rds_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,Id_sim)
+ error_cal ("nmos_6p0_nat_iv",nmos_vds,nmos6p0_nat_vgs,Rds_sim)
+ error_cal ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,Rds_sim)
+ error_cal ("nmos_3p3_sab_iv",nmos_vds,nmos3p3_vgs,Id_sim)
+ error_cal ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,Id_sim)
+ error_cal ("pmos_3p3_sab_iv",pmos_vds,pmos3p3_vgs,Rds_sim)
+ error_cal ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,Id_sim)
+ error_cal ("nmos_6p0_sab_iv",nmos_vds,nmos6p0_vgs,Rds_sim)
+ error_cal ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,Id_sim)
+ error_cal ("pmos_6p0_sab_iv",pmos_vds,pmos6p0_vgs,Rds_sim)
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/moscap_c/device_netlists/moscap.spice b/models/xyce/testing/regression/moscap_c/device_netlists/moscap.spice
new file mode 100644
index 0000000..132247d
--- /dev/null
+++ b/models/xyce/testing/regression/moscap_c/device_netlists/moscap.spice
@@ -0,0 +1,30 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+.param volt = -3.3
+V1 in 0 dc {volt} ac 1
+R1 in out 1G
+xcn out 0 {{device}} c_length={{length}}u c_width={{width}}u l={{length}}u w={{width}}u
+
+.meas AC freq when Vdb(out)=-3 PRECISION=15
+
+
+
+
+*****************
+** Analysis
+*****************
+
+.ac dec 10 1 10G
+.step volt {{voltage}}
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" moscap_{{corner}}
+
+.end
+
+
diff --git a/models/xyce/testing/regression/moscap_c/device_netlists/run_nmoscap_3p3_independently.spice b/models/xyce/testing/regression/moscap_c/device_netlists/run_nmoscap_3p3_independently.spice
new file mode 100644
index 0000000..97c50cf
--- /dev/null
+++ b/models/xyce/testing/regression/moscap_c/device_netlists/run_nmoscap_3p3_independently.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Ich 0 p dc 10u
+
+
+xcn p 0 nmoscap_3p3 L=50u W=50u
+* PJ=40u
+
+
+*****************
+** Analysis
+*****************
+.ic v(p)=0.0
+.tran 10ns 2000us
+
+.meas tran CV FIND par('(10.0e-6 * time / v(p))*1.0e15') AT=2000us
+
+
+
+.include "../../design.xyce"
+.lib "../../sm141064.xyce" moscap_typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/moscap_c/models_regression.py b/models/xyce/testing/regression/moscap_c/models_regression.py
new file mode 100644
index 0000000..731da96
--- /dev/null
+++ b/models/xyce/testing/regression/moscap_c/models_regression.py
@@ -0,0 +1,224 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device,vn,d_in, cv_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ if i == 0 :
+ # measured cv
+ col_list = [f"Vj",f"moscap_{corner}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+ else:
+ # measured cv
+ col_list = [f"Vj",f"moscap_{corner}.{i}"]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"Vj",f"moscap_{corner}"]
+ df_measured.to_csv(f"{dirpath}/measured_{cv_sim}/{i-start}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated(device,vn,d_in,cv_sim, corner,start,voltage):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ netlist_tmp = f"./device_netlists/moscap.spice"
+
+ # Extracting measured values for each W & L
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{cv_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner , voltage = voltage))
+ netlist_path = f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = []
+ # Writing simulated data
+ for j in range(len([x for x in os.listdir(f"{dirpath}/{device}_netlists_{cv_sim}") if f"{i-start}_{device}_netlist_w{width}_l{length}.spice.ma" in x])):
+ with open(f"{dirpath}/{device}_netlists_{cv_sim}/{i-start}_{device}_netlist_w{width}_l{length}.spice.ma{j}") as f:
+ cap = 1000000/(float(next(f).replace("FREQ = ", ""))*2*np.pi)
+ df_simulated.append(cap)
+
+ # zero array to append in it shaped (vn_sweeps, number of trials + 1)
+ new_array = np.zeros((len(df_simulated), 2))
+ new_array[:len(df_simulated), 0] = df_simulated
+ new_array[:len(df_simulated), 1] = df_simulated
+
+ # Writing final simulated data
+ df_simulated = pd.DataFrame(new_array)
+ df_simulated.columns = [f"Vj",f"moscap_{corner}"]
+ df_simulated.to_csv(f"{dirpath}/simulated_{cv_sim}/{i-start}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal(device,vn,d_in,Id_sim, corner,start):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{Id_sim}_{corner}"
+ df_final = pd.DataFrame()
+ for i in range (start,4+start):
+ if i == 0+start: width = 50 ; length = 50
+ if i == 1+start: width = 1 ; length = 1
+ if i == 2+start: width = 50 ; length = 1
+ if i == 3+start: width = 1 ; length = 50
+
+ measured = pd.read_csv(f"{dirpath}/measured_{Id_sim}/{i-start}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{Id_sim}/{i-start}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 1]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{Id_sim}/{i-start}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"moscap_{corner}"].mean())
+ # Max error
+ max_error = df_error[f"moscap_{corner}"].max()
+
+ df_final_ = {'Run no.': f'{i-start}', 'Device name': f'{dirpath}', 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{Id_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{Id_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{Id_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+
+def main():
+
+ # 3p3
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_3p3" , "pmoscap_3p3" , "nmoscap_3p3_b" , "pmoscap_3p3_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ voltage = ["-3.3 3.3 0.1","-6.6 6.0 0.1"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/moscap_cv_3p3.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start,voltage[0])
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+ # 6p0
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_6p0" , "pmoscap_6p0" , "nmoscap_6p0_b" , "pmoscap_6p0_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ dirpath = f"{device}_{cv_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{cv_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Cap/moscap_cv_6p0.nl_out.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{cv_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{cv_sim}",exist_ok=False)
+
+ ext_measured (device,cap_vn,cap_in, cv_sim, corner,start)
+ ext_simulated(device,cap_vn,cap_in,cv_sim, corner,start,voltage[1])
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+ # 3p3
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_3p3" , "pmoscap_3p3" , "nmoscap_3p3_b" , "pmoscap_3p3_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+ # 6p0
+ corners = ["typical","ff","ss"]
+ devices = ["nmoscap_6p0" , "pmoscap_6p0" , "nmoscap_6p0_b" , "pmoscap_6p0_b"]
+ measure = ["cv","corners", "CV (fF)"]
+ start = 0
+ for corner in corners:
+ for device in devices:
+ # Folder structure of measured values
+ cv_sim, cap_vn, cap_in = measure[0], measure[1], measure[2]
+ error_cal (device,cap_vn,cap_in,cv_sim, corner,start)
+ start = start + 4
+ start = 0
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_a.spice b/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_a.spice
new file mode 100644
index 0000000..a5f7272
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_a.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 DC 1
+
+xrn n 0 {{device}} L={{width}}u W={{length}}u
+
+
+*****************
+** Analysis
+*****************
+.OP
+.STEP TEMP 25 -60 200
+.print DC FORMAT=CSV file={{device}}_r_{{corner}}/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv {(V(n)*{{length}}u)/(-I(Vn)*{{width}}u)}
+
+
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" res_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_b.spice b/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_b.spice
new file mode 100644
index 0000000..82ccc77
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/2term_res_b.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 DC 1
+
+xrn n 0 {{device}} L={{width}}u W={{length}}u
+
+
+*****************
+** Analysis
+*****************
+.OP
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file={{device}}_r_{{corner}}_temp/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv {(V(n)*{{length}}u)/(-I(Vn)*{{width}}u)}
+
+
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" res_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_a.spice b/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_a.spice
new file mode 100644
index 0000000..ad0f4eb
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_a.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 DC 1
+
+xrn n 0 0 {{device}} L={{width}}u W={{length}}u
+
+
+*****************
+** Analysis
+*****************
+.OP
+.STEP TEMP 25 -60 200
+.print DC FORMAT=CSV file={{device}}_r_{{corner}}/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv {(V(n)*{{length}}u)/(-I(Vn)*{{width}}u)}
+
+
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" res_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_b.spice b/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_b.spice
new file mode 100644
index 0000000..b7af642
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/3term_res_b.spice
@@ -0,0 +1,25 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 DC 1
+
+xrn n 0 0 {{device}} L={{width}}u W={{length}}u
+
+
+*****************
+** Analysis
+*****************
+.OP
+.STEP TEMP {{temp}} -60 200
+.print DC FORMAT=CSV file={{device}}_r_{{corner}}_temp/simulated_r/{{i}}_simulated_w{{width}}_l{{length}}.csv {(V(n)*{{length}}u)/(-I(Vn)*{{width}}u)}
+
+
+
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" res_{{corner}}
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/run_2term_res.spice b/models/xyce/testing/regression/resistor_r/device_netlists/run_2term_res.spice
new file mode 100644
index 0000000..d7e0b81
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/run_2term_res.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 dc 1
+
+r1 n p 1M
+xrn p 0 rm1 L=100u W=100u
+* PJ=40u
+
+* .print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/100u*100u')
+
+*****************
+** Analysis
+*****************
+.OP
+
+.print DC FORMAT=CSV file=result.csv {(1M*V(p)/(V(n)-V(p)))/100u*100u}
+
+
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" res_typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/device_netlists/run_3term_res.spice b/models/xyce/testing/regression/resistor_r/device_netlists/run_3term_res.spice
new file mode 100644
index 0000000..c1853ca
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/device_netlists/run_3term_res.spice
@@ -0,0 +1,28 @@
+*Xyce Common Source Circuit
+** library calling
+
+
+*****************
+** main netlist
+*****************
+Vn n 0 dc 1
+
+*r1 n p 1MEG
+xrn n 0 0 ppolyf_s L=50u W=150u
+* PJ=40u
+
+* .print dc Rs=par('(1M*V(p)/(V(n)-V(p)))/100u*100u')
+
+*****************
+** Analysis
+*****************
+.OP
+* .DC Vn 1 1 0.1
+* .print DC FORMAT=CSV file=result.csv {(1MEG*V(p)/(V(n)-V(p)))/50u*150u}
+.print DC FORMAT=CSV file=result.csv {(V(n)*150u)/(-I(Vn)*50u)}
+
+
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" res_typical
+
+.end
\ No newline at end of file
diff --git a/models/xyce/testing/regression/resistor_r/models_regression.py b/models/xyce/testing/regression/resistor_r/models_regression.py
new file mode 100644
index 0000000..92d38b4
--- /dev/null
+++ b/models/xyce/testing/regression/resistor_r/models_regression.py
@@ -0,0 +1,324 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from fileinput import filename
+from re import T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured_a(device,vn,d_in, r_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ # measured r
+ col_list = [f"{vn}",f"{d_in}_{corner} Rev9 "]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}","value"]
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_measured_b(device,vn,d_in, r_sim, corner):
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ # measured r
+ col_list = [f"{vn}",f"{d_in}_{corner} Rev9 "]
+ df_measured = pd.read_csv(f"{dirpath}/{device}.csv",usecols=col_list)
+ df_measured.columns = [f"{vn}","value"]
+ df_measured.loc[i:i].to_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv", index=False)
+
+def ext_simulated_a(device,vn,d_in,r_sim, corner,sign):
+
+ if "rm" in device or "tm" in device:
+ netlist_tmp = f"./device_netlists/2term_res_a.spice"
+ else:
+ netlist_tmp = f"./device_netlists/3term_res_a.spice"
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+ temp = 25
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{r_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, width = width, length = length , corner = corner, i = i , sign = sign))
+ netlist_path = f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",header=0)
+ # df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+ # Writing final simulated data
+ df_simulated.columns = [f"value"]#,"value"]
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+def ext_simulated_b(device,vn,d_in,r_sim, corner,sign):
+
+ if "rm" in device or "tm" in device:
+ netlist_tmp = f"./device_netlists/2term_res_b.spice"
+ else:
+ netlist_tmp = f"./device_netlists/3term_res_b.spice"
+
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{dirpath}/{device}_netlists_{r_sim}",exist_ok=True)
+ with open(f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice", "w") as netlist:
+ netlist.write(tmpl.render(device = device, temp = temp , width = width, length = length , corner = corner, i = i , sign = sign))
+ netlist_path = f"{dirpath}/{device}_netlists_{r_sim}/{i}_{device}_netlist_w{width}_l{length}.spice"
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",header=0)
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+ # Writing final simulated data
+ df_simulated.columns = [f"value"]#,"value"]
+ df_simulated.to_csv(f"{dirpath}/simulated_r/{i}_simulated_w{width}_l{length}.csv",index= False)
+
+def error_cal_a(device,vn,d_in,r_sim, corner):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["w (um)" , "l (um)"])
+ loops = dimensions["l (um)"].count()
+ temp = 25
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ width = dimensions["w (um)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+
+ measured = pd.read_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{r_sim}/{i}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 0]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{r_sim}/{i}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"value"].mean())
+ # Max error
+ max_error = df_error[f"value"].max()
+
+ df_final_ = {'Run no.': f'{i}', 'Device name': f'{dirpath}', 'Temperature': temp, 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{r_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{r_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{r_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def error_cal_b(device,vn,d_in,r_sim, corner):
+
+ df_final = pd.DataFrame()
+ # Get dimensions used for each device
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ dimensions = pd.read_csv(f"{dirpath}/{device}.csv",usecols=["Temperature (C)" , "l (um)"])
+ if "rm" in device or "tm" in device:
+ loops = dimensions["l (um)"].count()
+ else:
+ loops = 11
+
+ # Extracting measured values for each W & L
+ for i in range (0,loops):
+ temp = dimensions["Temperature (C)"].iloc[i]
+ length = dimensions["l (um)"].iloc[i]
+ if "rm" in device or "tm" in device or "_u" in device:
+ width = length
+ else:
+ width = length/2
+
+ measured = pd.read_csv(f"{dirpath}/measured_{r_sim}/{i}_measured_w{width}_l{length}.csv")
+ simulated = pd.read_csv(f"{dirpath}/simulated_{r_sim}/{i}_simulated_w{width}_l{length}.csv")
+
+ error_1 = round (100 * abs((abs(measured.iloc[:, 1]) - abs(simulated.iloc[:, 0]))/abs(measured.iloc[:, 1])),8)
+
+ df_error = pd.DataFrame(data=[measured.iloc[:, 0],error_1]).transpose()
+ df_error.to_csv(f"{dirpath}/error_{r_sim}/{i}_{device}_error_w{width}_l{length}.csv",index= False)
+
+ # Mean error
+ mean_error = (df_error[f"value"].mean())
+ # Max error
+ max_error = df_error[f"value"].max()
+
+ df_final_ = {'Run no.': f'{i}', 'Device name': f'{dirpath}', 'Temperature': temp, 'Width': f'{width}', 'Length': f'{length}', 'Simulated_Val':f'{r_sim}','Mean error%':f'{"{:.2f}".format(mean_error)}', 'Max error%':f'{"{:.2f}".format(max_error)} '}
+ df_final = df_final.append(df_final_, ignore_index = True)
+ # Max mean error
+ print (df_final)
+ df_final.to_csv (f"{dirpath}/Final_report_{r_sim}.csv", index = False)
+ out_report = pd.read_csv (f"{dirpath}/Final_report_{r_sim}.csv")
+ print ("\n",f"Max. mean error = {out_report['Mean error%'].max()}%")
+ print ("=====================================================================================================================================================")
+
+def main():
+
+ # res W&L var.
+ corners_a = ["typical","ff","ss"]
+
+ devices_a = ["nplus_u" , "pplus_u" , "nplus_s" , "pplus_s" , "npolyf_u" , "ppolyf_u" , "npolyf_s" , "ppolyf_s" , "ppolyf_u_1k" , "ppolyf_u_2k" , "ppolyf_u_1k_6p0" ,
+ "ppolyf_u_2k_6p0" , "ppolyf_u_3k" , "rm1" , "rm2" , "rm3" , "tm6k" , "tm9k" , "tm11k" , "tm30k" , "nwell"]
+
+ dev_data_a = ["RES01a-wl-nplus_u.nl_out" ,"RES02a-wl-pplus_u.nl_out" ,"RES03a-wl-nplus_s.nl_out" , "RES04a-wl-pplus_s.nl_out" ,
+ "RES06a-wl-npolyf_u.nl_out" ,"RES07a-wl-ppolyf_u.nl_out" ,"RES08a-wl-npolyf_s.nl_out" , "RES09a-wl-ppolyf_s.nl_out" , "RES10a-wl-ppolyf_u_1k.nl_out",
+ "RES11a-wl-ppolyf_u_2k.nl_out" ,"RES12a-wl-ppolyf_u_1k_6p0.nl_o" ,"RES13a-wl-ppolyf_u_2k_6p0.nl_o" , "RES14a-wl-ppolyf_u_3k.nl_out" ,
+ "RES15a-wl-rm1.nl_out" , "RES16a-wl-rm2.nl_out" , "RES17a-wl-rm3.nl_out" , "RES18a-wl-tm6k.nl_out" , "RES19a-wl-tm9k.nl_out" ,
+ "RES20a-wl-tm11k.nl_out" , "RES21a-wl-tm30k.nl_out" ,"RES05a-wl-nwell.nl_out"]
+
+ sign_a = ["+" , "-" , "+" , "-" , "+" , "+" , "-" , "+" , "-" , "-" , "-" , "-" , "-" , "-" , "+" , "+" , "+" , "+" , "+" , "+", "+", ""]
+ measure_a = ["r","corners", "res"]
+
+ for corner in corners_a:
+ for i,device in enumerate(devices_a):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_a[0], measure_a[1], measure_a[2]
+ dirpath = f"{device}_{r_sim}_{corner}"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{r_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Resistor/{dev_data_a[i]}.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{r_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{r_sim}",exist_ok=False)
+
+ ext_measured_a (device,res_vn,res_in, r_sim, corner)
+ ext_simulated_a(device,res_vn,res_in,r_sim, corner,sign_a[i])
+ error_cal_a (device,res_vn,res_in,r_sim, corner)
+
+
+ # res temp_var
+ corners_b = ["typical","ff","ss"]
+ devices_b = ["nplus_u" , "nplus_s", "pplus_u" , "pplus_s" , "nwell" , "npolyf_u" , "ppolyf_u" , "npolyf_s" , "ppolyf_s" , "ppolyf_u_1k" , "ppolyf_u_2k" , "ppolyf_u_1k_6p0" , "ppolyf_u_2k_6p0" , "ppolyf_u_3k" ,
+ "rm1" , "rm2", "rm3" , "tm6k" , "tm9k" , "tm11k", "tm30k"]
+ dev_data_b = ["RES01b-temp-nplus_u.nl_out" , "RES03b-temp-nplus_s.nl_out", "RES02b-temp-pplus_u.nl_out" , "RES04b-temp-pplus_s.nl_out" , "RES05b-temp-nwell.nl_out" ,
+ "RES06b-temp-npolyf_u.nl_out" , "RES07b-temp-ppolyf_u.nl_out" , "RES08b-temp-npolyf_s.nl_out" , "RES09b-temp-ppolyf_s.nl_out" , "RES10b-temp-ppolyf_u_1k.nl_out" ,
+ "RES11b-temp-ppolyf_u_2k.nl_out" , "RES12b-temp-ppolyf_u_1k_6p0.nl" , "RES13b-temp-ppolyf_u_2k_6p0.nl" , "RES14b-temp-ppolyf_u_3k.nl_out" ,
+ "RES15b-temp-rm1.nl_out", "RES16b-temp-rm2.nl_out" , "RES17b-temp-rm3.nl_out" ,
+ "RES18b-temp-tm6k.nl_out", "RES19b-temp-tm9k.nl_out" , "RES20b-temp-tm11k.nl_out", "RES21b-temp-tm30k.nl_out"]
+
+ sign_b = ["+" , "+", "-" , "-" , "+" , "+" , "-" ,"+" , "-" , "-" , "-" , "-" , "-" , "-" , "+" , "+" , "+" , "+" , "+" , "+", "+"]
+ measure_b = ["r","corners", "res"]
+
+ for corner in corners_b:
+ for i,device in enumerate(devices_b):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_b[0], measure_b[1], measure_b[2]
+ dirpath = f"{device}_{r_sim}_{corner}_temp"
+ if os.path.exists(dirpath) and os.path.isdir(dirpath):
+ shutil.rmtree(dirpath)
+ os.makedirs(f"{dirpath}/measured_{r_sim}",exist_ok=False)
+
+ # From xlsx to csv
+ read_file = pd.read_excel (f"../../180MCU_SPICE_DATA/Resistor/{dev_data_b[i]}.xlsx")
+ read_file.to_csv (f"{dirpath}/{device}.csv", index = False, header=True)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{dirpath}/simulated_{r_sim}",exist_ok=False)
+ os.makedirs(f"{dirpath}/error_{r_sim}",exist_ok=False)
+
+ ext_measured_b (device,res_vn,res_in, r_sim, corner)
+ ext_simulated_b(device,res_vn,res_in,r_sim, corner,sign_b[i])
+ error_cal_b (device,res_vn,res_in,r_sim, corner)
+
+ for corner in corners_a:
+ for i,device in enumerate(devices_a):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_a[0], measure_a[1], measure_a[2]
+ error_cal_a (device,res_vn,res_in,r_sim, corner)
+
+
+ for corner in corners_b:
+ for i,device in enumerate(devices_b):
+ # Folder structure of measured values
+ r_sim, res_vn, res_in = measure_b[0], measure_b[1], measure_b[2]
+ error_cal_b (device,res_vn,res_in,r_sim, corner)
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice
new file mode 100644
index 0000000..9eb17df
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1.spice
@@ -0,0 +1,43 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vin I 0 dc pulse(0 5 100p 1p 1p 100p 200p)
+Vdd VDD 0 dc {{volt}}
+
+* Main circuit
+X1 I ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__inv_1
+
+* Temperature set
+.STEP TEMP {{temp}} -60 200
+
+* Analyses
+.tran 1p 200p
+.meas tran high_in FIND V(ZN) AT=100p
+.meas tran low_in FIND V(ZN) AT=200p
+
+.print TRAN FORMAT=CSV file=inv/simulated/inv_{{process}}_{{temp}}c_{{volt}}v.csv high_in low_in
+
+
+* Libraries calling
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__inv_1 I ZN VDD VNW VPW VSS
+xM_i_0 ZN I VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_1 ZN I VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice
new file mode 100644
index 0000000..c4bd339
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__inv_1_run.spice
@@ -0,0 +1,42 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vin I 0 dc pulse(0 5 100p 1p 1p 100p 200p)
+Vdd VDD 0 dc 5
+
+* Main circuit
+X1 I ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__inv_1
+
+* Temperature set
+.STEP TEMP 25 -60 200
+
+* Analyses
+.tran 1p 200p
+.meas tran high_in FIND V(ZN) AT=100p
+.meas tran low_in FIND V(ZN) AT=200p
+.print TRAN FORMAT=CSV file=inv_simulated.csv {high_in} {low_in}
+
+
+* Libraries calling
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__inv_1 I ZN VDD VNW VPW VSS
+xM_i_0 ZN I VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_1 ZN I VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice
new file mode 100644
index 0000000..53ce93d
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1.spice
@@ -0,0 +1,47 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc {{volt}}
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+
+* Main circuit
+X1 I1 I2 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__nand2_1
+
+* Temperature set
+.STEP TEMP {{temp}} -60 200
+
+* Analyses
+.tran 1p 800p
+.meas tran o0 FIND V(ZN) AT=200p
+.meas tran o1 FIND V(ZN) AT=400p
+.meas tran o2 FIND V(ZN) AT=600p
+.meas tran o3 FIND V(ZN) AT=800p
+.print TRAN FORMAT=CSV file=nand2/simulated/nand2_{{process}}_{{temp}}c_{{volt}}v.csv o0 o1 o2 o3
+
+
+* Libraries calling
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__nand2_1 A1 A2 ZN VDD VNW VPW VSS
+xM_i_1 net_0 A2 VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_0 ZN A1 net_0 VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_3 ZN A2 VDD VNW pmos_6p0 W=1.13e-06 L=5e-07
+xM_i_2 VDD A1 ZN VNW pmos_6p0 W=1.13e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice
new file mode 100644
index 0000000..6f7509c
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__nand2_1_run.spice
@@ -0,0 +1,48 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc 5
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+
+* Main circuit
+X1 I1 I2 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__nand2_1
+
+* Temperature set
+.temp 25
+.options tnom=25
+
+* Analyses
+.tran 1p 800p
+.meas tran o0 FIND V(ZN) AT=200p
+.meas tran o1 FIND V(ZN) AT=400p
+.meas tran o2 FIND V(ZN) AT=600p
+.meas tran o3 FIND V(ZN) AT=800p
+.print TRAN FORMAT=CSV file=nand2_simulated.csv o0 o1 o2 o3
+
+
+* Libraries calling
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__nand2_1 A1 A2 ZN VDD VNW VPW VSS
+xM_i_1 net_0 A2 VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_0 ZN A1 net_0 VPW nmos_6p0 W=8.2e-07 L=6e-07
+xM_i_3 ZN A2 VDD VNW pmos_6p0 W=1.13e-06 L=5e-07
+xM_i_2 VDD A1 ZN VNW pmos_6p0 W=1.13e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice
new file mode 100644
index 0000000..01a7534
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1.spice
@@ -0,0 +1,56 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc {{volt}}
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+Vin3 I3 0 dc pulse(0 5 800p 1p 1p 800p 1600p)
+
+* Main circuit
+X1 I1 I2 I3 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__or3_1
+
+* Temperature set
+.STEP TEMP {{temp}} -60 200
+
+* Analyses
+.tran 1p 1600p
+.meas tran o0 FIND V(ZN) AT=200p
+.meas tran o1 FIND V(ZN) AT=400p
+.meas tran o2 FIND V(ZN) AT=600p
+.meas tran o3 FIND V(ZN) AT=800p
+.meas tran o4 FIND V(ZN) AT=1000p
+.meas tran o5 FIND V(ZN) AT=1200p
+.meas tran o6 FIND V(ZN) AT=1400p
+.meas tran o7 FIND V(ZN) AT=1600p
+.print TRAN FORMAT=CSV file=or3/simulated/or3_{{process}}_{{temp}}c_{{volt}}v.csv o0 o1 o2 o3 o4 o5 o6 o7
+
+
+* Libraries calling
+.include "../../../../../design.xyce"
+.lib "../../../../../sm141064.xyce" {{process}}
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__or3_1 A1 A2 A3 Z VDD VNW VPW VSS
+M_i_2 VSS A1 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_3 Z_neg A2 VSS VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_4 VSS A3 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_0 Z Z_neg VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+M_i_5 net_0 A1 Z_neg VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_6 net_1 A2 net_0 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_7 VDD A3 net_1 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_1 Z Z_neg VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice
new file mode 100644
index 0000000..bfa90e9
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/device_netlists/gf180mcu_fd_sc_mcu7t5v0__or3_1_run.spice
@@ -0,0 +1,59 @@
+* Copyright 2022 GlobalFoundries PDK Authors
+*
+* 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
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+* Sources
+Vdd VDD 0 dc 5
+Vin1 I1 0 dc pulse(0 5 200p 1p 1p 200p 400p)
+Vin2 I2 0 dc pulse(0 5 400p 1p 1p 400p 800p)
+Vin3 I3 0 dc pulse(0 5 800p 1p 1p 800p 1600p)
+
+* Main circuit
+X1 I1 I2 I3 ZN VDD VDD 0 0 gf180mcu_fd_sc_mcu7t5v0__or3_1
+
+* Temperature set
+.temp 25
+.options tnom=25
+
+* Analyses
+.control
+tran 1p 1600p
+meas tran o0 FIND V(ZN) AT=200p
+meas tran o1 FIND V(ZN) AT=400p
+meas tran o2 FIND V(ZN) AT=600p
+meas tran o3 FIND V(ZN) AT=800p
+meas tran o4 FIND V(ZN) AT=1000p
+meas tran o5 FIND V(ZN) AT=1200p
+meas tran o6 FIND V(ZN) AT=1400p
+meas tran o7 FIND V(ZN) AT=1600p
+
+wrdata or3/or3_simulated.csv {o0} {o1} {o2} {o3} {o4} {o5} {o6} {o7}
+.endc
+
+* Libraries calling
+.include "../../../../design.xyce"
+.lib "../../../../sm141064.xyce" typical
+
+.SUBCKT gf180mcu_fd_sc_mcu7t5v0__or3_1 A1 A2 A3 Z VDD VNW VPW VSS
+M_i_2 VSS A1 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_3 Z_neg A2 VSS VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_4 VSS A3 Z_neg VPW nmos_6p0 W=4e-07 L=6e-07
+M_i_0 Z Z_neg VSS VPW nmos_6p0 W=8.2e-07 L=6e-07
+M_i_5 net_0 A1 Z_neg VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_6 net_1 A2 net_0 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_7 VDD A3 net_1 VNW pmos_6p0 W=5.6e-07 L=5e-07
+M_i_1 Z Z_neg VDD VNW pmos_6p0 W=1.22e-06 L=5e-07
+.ENDS
+
+
+.end
diff --git a/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py
new file mode 100644
index 0000000..c61d72a
--- /dev/null
+++ b/models/xyce/testing/sc_regression/gf180mcu_fd_sc_mcu7t5v0/models_regression.py
@@ -0,0 +1,140 @@
+"""
+Usage:
+ models_regression.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+from cmath import inf
+from re import L, T
+from docopt import docopt
+import pandas as pd
+import numpy as np
+import os
+from jinja2 import Template
+import concurrent.futures
+import shutil
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def ext_measured(device, table):
+
+ # Generate CSVs with truth tables
+ df = pd.DataFrame(data=table)
+ df.set_index(df.columns[0])
+ new_header = df.iloc[0]
+ df = df[1:]
+ df.columns = new_header
+ df.to_csv(f"{device}/{device}_measured.csv", index = False)
+
+def ext_simulated(device, processes, volts, temps):
+
+ # Get all corners simulated
+ for process in processes:
+ for volt in volts:
+ for temp in temps:
+ with open(f"device_netlists/gf180mcu_fd_sc_mcu7t5v0__{device}_1.spice") as f:
+ tmpl = Template(f.read())
+ netlist_path = f"{device}/{device}_netlists/{device}_{process}_{temp}c_{volt}v.spice"
+ with open(netlist_path, "w") as netlist:
+ netlist.write(tmpl.render(process = process, volt = volt , temp = temp ))
+
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ df_simulated = pd.read_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv",header=0)
+ results = []
+ for i, col in enumerate(df_simulated.columns):
+ if df_simulated.iloc[-1].iloc[i] > 2.5:
+ results.append(1)
+ else:
+ results.append(0)
+ df_measured = pd.read_csv(f"{device}/{device}_measured.csv",header=0)
+ df = df_measured
+ df_measured.drop(df_measured.columns[len(df_measured.columns)-1], axis=1, inplace=True)
+ df_measured['output'] = results[1:]
+ df_measured.to_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv",index= False)
+
+def error_cal(device, processes, volts, temps):
+
+ print (f"\n\nSimulation results of {device}")
+ measured = pd.read_csv(f"{device}/{device}_measured.csv")
+ for process in processes:
+ for volt in volts:
+ for temp in temps:
+ simulated = pd.read_csv(f"{device}/simulated/{device}_{process}_{temp}c_{volt}v.csv")
+
+ res = (measured == simulated).all().all()
+ print ("{:^5s} in PVT of {:^7s}, {:^3s}V, {:^3s}C functional simulation = {}".format(device, process, volt, temp, res))
+ print ("================================================================================================\n")
+
+def main():
+
+ devices = ["inv","nand2","or3"]
+
+ # Generate truth tables data
+ inv_table = [["input","output"],
+ [0,1],
+ [1,0]]
+
+ nand2_table = [["input1","input2","output"],
+ [0,0,1],
+ [0,1,1],
+ [1,0,1],
+ [1,1,0]]
+
+ or3_table = [["input1","input2","input3","output"],
+ [0,0,0,0],
+ [0,0,1,1],
+ [0,1,0,1],
+ [0,1,1,1],
+ [1,0,0,1],
+ [1,0,1,1],
+ [1,1,0,1],
+ [1,1,1,1]]
+
+ tables = [inv_table,nand2_table,or3_table]
+
+ processes = ["typical","ff","ss"]
+ volts = ["5", "4.5", "5.5"]
+ temps = ["25", "-40", "125"]
+
+ for i, device in enumerate(devices):
+ # Folder structure of measured values
+ if os.path.exists(device) and os.path.isdir(device):
+ shutil.rmtree(device)
+ os.makedirs(device)
+
+ # Folder structure of simulated values
+ os.makedirs(f"{device}/{device}_netlists",exist_ok=True)
+ os.makedirs(f"{device}/simulated",exist_ok=True)
+
+ ext_measured (device, tables[i])
+ # =========== Simulate ==============
+ ext_simulated(device, processes, volts, temps)
+
+ # ============ Results ==============
+ error_cal (device, processes, volts, temps)
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='comparator: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/models/xyce/testing/smoke_test/inv_xyce.spice b/models/xyce/testing/smoke_test/inv_xyce.spice
new file mode 100644
index 0000000..ada6d54
--- /dev/null
+++ b/models/xyce/testing/smoke_test/inv_xyce.spice
@@ -0,0 +1,26 @@
+** INV_tb
+Vdd Vdd 0 3.3
+Vin Vin 0 dc pulse(0 3.3 0 50p 50p 150p 300p)
+X1 Vdd Vout Vin 0 INV
+
+.measure tran tphl TRIG v(Vin) VAL={0.5*3.3} RISE=2 TARG v(Vout) VAL={0.5*3.3} FALL=2
+.measure tran tplh TRIG v(Vin) VAL={0.5*3.3} FALL=1 TARG v(Vout) VAL={0.5*3.3} RISE=1
+.measure tran tpd param = {(tplh+tphl)/2}
+
+.STEP TEMP {{temp}} -60 200
+.tran 5p 800p
+.print tran FORMAT=CSV file={{run_path}}/simulation/inv_W{{width}}_L{{length}}_T{{temp}}_{{corner}}.csv {tpd}
+
+
+.include "../../design.xyce"
+.lib "../../sm141064.xyce" {{corner}}
+
+
+.subckt INV VDD Vout Vin GND
+XM1 Vout Vin GND GND nmos_3p3 W={{width}}u L={{length}}u
+*ad={{AD}}u pd={{PD}}u as={{AS}}u ps={{PS}}u
+XM2 Vout Vin VDD VDD pmos_3p3 W={{width_p}}u L={{length}}u
+*ad={{AD_p}}u pd={{PD_p}}u as={{AD_p}}u ps={{PD_p}}u
+.ends
+
+.end
diff --git a/models/xyce/testing/smoke_test/xyce_smoke_test.py b/models/xyce/testing/smoke_test/xyce_smoke_test.py
new file mode 100644
index 0000000..129157f
--- /dev/null
+++ b/models/xyce/testing/smoke_test/xyce_smoke_test.py
@@ -0,0 +1,102 @@
+"""
+Usage:
+ smoke_test.py [--num_cores=<num>]
+
+ -h, --help Show help text.
+ -v, --version Show version.
+ --num_cores=<num> Number of cores to be used by simulator
+"""
+
+import re
+from docopt import docopt
+import pandas as pd
+import os
+from jinja2 import Template
+import concurrent.futures
+import datetime
+import warnings
+warnings.simplefilter(action='ignore', category=FutureWarning)
+
+def call_simulator(file_name):
+ """Call simulation commands to perform simulation.
+ Args:
+ file_name (str): Netlist file name.
+ """
+ os.system(f"Xyce -hspice-ext all {file_name} -l {file_name}.log")
+
+def get_sizes(models_path):
+ sizes = []
+ with open(models_path, "r") as f:
+ device_model = f.read()
+ # for j, corner in enumerate(corners):
+ for i in range(16):
+ data = device_model.split(f".MODEL nmos_3p3.{i} NMOS")
+ data = data[1].split("+ LEVEL=14")
+ dimensions = re.findall("LMAX=([0-9e.-]+)[\w+\s=.{}-]+LMIN=([0-9e.-]+)[\w+\s=.{}-]+WMAX=([0-9e.-]+)[\w+\s=.{}-]+WMIN=([0-9e.-]+)[\w+\s=.{}-]+", data[0])
+ sizes.append(dimensions)
+ return sizes
+
+def get_results(run_path,sizes,temp,corner):
+ netlist_tmp = f"./inv_xyce.spice"
+ width = float(sizes[0][3]) * 1000000
+ width_p = width * 1.5
+ length = float(sizes[0][1]) * 1000000
+ # AD = width * 0.24
+ # AD_p = AD * 1.5
+ # PD = 2 * (width + 0.24)
+ # PD_p = width + PD
+ # AS = AD
+ # PS = PD
+ with open(netlist_tmp) as f:
+ tmpl = Template(f.read())
+ os.makedirs(f"{run_path}/netlists",exist_ok=True)
+ os.makedirs(f"{run_path}/simulation",exist_ok=True)
+ netlist_path = f"{run_path}/netlists/inv_W{width}_L{length}_T{temp}_{corner}.spice"
+ with open(netlist_path, "w") as netlist:
+ netlist.write(tmpl.render(corner = corner, width = width,length = length, temp = temp , run_path = run_path, width_p = width_p))#, AD = AD , PD = PD , AS = AS , PS = PS, AD_p = AD_p, PD_p = PD_p ))
+ # Running ngspice for each netlist
+ with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:
+ executor.submit(call_simulator, netlist_path)
+
+ # Writing simulated data
+ df_simulated = pd.read_csv(f"{run_path}/simulation/inv_W{width}_L{length}_T{temp}_{corner}.csv",header=0)
+ return [f"W{width}_L{length}_T{temp}_{corner}",df_simulated["{TPD}"].iloc[-1]]
+
+def main():
+
+ models_path = "../../sm141064.xyce"
+ temps = ["25","-40","125"]
+ corners = ["typical","ff","ss","fs","sf"]#,"stat"]
+
+ time = f"{datetime.datetime.now()}".replace(" ", "_")
+ run_path = f"../run_smoke_{time}"
+ os.makedirs(run_path,exist_ok=True)
+
+ sizes = get_sizes(models_path)
+ results = []
+
+ for corner in corners:
+ for temp in temps:
+ for size in sizes:
+ results.append(get_results(run_path, size, temp, corner))
+
+ df_results = pd.DataFrame(results)
+ df_results.columns = ["run","tpd_result"]
+ df_results.to_csv(f"{run_path}/final_results.csv",index= False)
+
+ print (df_results)
+
+
+
+# # ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # Args
+ arguments = docopt(__doc__, version='smoke_test: 0.1')
+ workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
+
+ # Calling main function
+ main()
diff --git a/rules/klayout/drc/rule_decks/10v_ldnmos.drc b/rules/klayout/drc/rule_decks/10v_ldnmos.drc
new file mode 100644
index 0000000..da06d05
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/10v_ldnmos.drc
@@ -0,0 +1,529 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#--------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (10V LDNMOS) ----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+mvsd = polygons(210, 0 )
+ldmos_xtor = polygons(226, 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#-------------------10V LDNMOS-------------------
+#================================================
+
+# Rule MDN.1: Min MVSD width (for litho purpose). is 1µm
+logger.info("Executing rule MDN.1")
+mdn1_l1 = mvsd.width(1.um, euclidian).polygons(0.001)
+mdn1_l1.output("MDN.1", "MDN.1 : Min MVSD width (for litho purpose). : 1µm")
+mdn1_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_mdn_2a, unconnected_mdn_2b = conn_space(mvsd, 1, 2, euclidian)
+
+# Rule MDN.2a: Min MVSD space [Same Potential]. is 1µm
+logger.info("Executing rule MDN.2a")
+mdn2a_l1 = connected_mdn_2a
+mdn2a_l1.output("MDN.2a", "MDN.2a : Min MVSD space [Same Potential]. : 1µm")
+mdn2a_l1.forget
+
+# Rule MDN.2b: Min MVSD space [Diff Potential]. is 2µm
+logger.info("Executing rule MDN.2b")
+mdn2b_l1 = unconnected_mdn_2b
+mdn2b_l1.output("MDN.2b", "MDN.2b : Min MVSD space [Diff Potential]. : 2µm")
+mdn2b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule MDN.2b: Min MVSD space [Diff Potential]. is 2µm
+logger.info("Executing rule MDN.2b")
+mdn2b_l1 = mvsd.space(2.um, euclidian).polygons(0.001)
+mdn2b_l1.output("MDN.2b", "MDN.2b : Min MVSD space [Diff Potential]. : 2µm")
+mdn2b_l1.forget
+
+end #CONNECTIVITY_RULES
+
+gate_mdn = poly2.and(comp).inside(ldmos_xtor).inside(dualgate)
+# Rule MDN.3a: Min transistor channel length. is 0.6µm
+logger.info("Executing rule MDN.3a")
+mdn3a_l1 = gate_mdn.enclosing(mvsd, 0.6.um, euclidian).polygons(0.001)
+mdn3a_l1.output("MDN.3a", "MDN.3a : Min transistor channel length. : 0.6µm")
+mdn3a_l1.forget
+
+mvsd_mdn = mvsd.edges.and(ncomp).and(poly2)
+# Rule MDN.3b: Max transistor channel length.
+logger.info("Executing rule MDN.3b")
+mdn3b_l1 = poly2.edges.and(ncomp).or(mvsd_mdn).and(ldmos_xtor).and(dualgate).not(ngate.not(mvsd).edges.interacting(poly2.edges.and(ncomp).or(mvsd_mdn)).width(20.001.um).edges)
+mdn3b_l1.output("MDN.3b", "MDN.3b : Max transistor channel length.")
+mdn3b_l1.forget
+
+mvsd_mdn.forget
+# Rule MDN.4a: Min transistor channel width. is 4µm
+logger.info("Executing rule MDN.4a")
+mdn4a_l1 = gate_mdn.edges.not(mvsd).interacting(mvsd).width(4.um, euclidian).polygons(0.001)
+mdn4a_l1.output("MDN.4a", "MDN.4a : Min transistor channel width. : 4µm")
+mdn4a_l1.forget
+
+# Rule MDN.4b: Max transistor channel width.
+logger.info("Executing rule MDN.4b")
+mdn4b_l1 = gate_mdn.not(mvsd).interacting(nplus).not_interacting(gate_mdn.edges.not(mvsd).not(poly2.edges).width(50.001.um).polygons)
+mdn4b_l1.output("MDN.4b", "MDN.4b : Max transistor channel width.")
+mdn4b_l1.forget
+
+gate_mdn.forget
+pcomp_mdn5a = pcomp.not_interacting(ncomp).inside(ldmos_xtor).inside(dualgate)
+# Rule MDN.5ai: Min PCOMP (Pplus AND COMP) space to LDNMOS Drain MVSD (source and body tap non-butted). PCOMP (Pplus AND COMP) intercept with LDNMOS Drain MVSD is not allowed.
+logger.info("Executing rule MDN.5ai")
+mdn5ai_l1 = mvsd.and(pcomp_mdn5a).or(pcomp_mdn5a.separation(mvsd, 1.um, euclidian).polygons(0.001))
+mdn5ai_l1.output("MDN.5ai", "MDN.5ai : Min PCOMP (Pplus AND COMP) space to LDNMOS Drain MVSD (source and body tap non-butted). PCOMP (Pplus AND COMP) intercept with LDNMOS Drain MVSD is not allowed.")
+mdn5ai_l1.forget
+
+pcomp_mdn5a.forget
+# Rule MDN.5aii: Min PCOMP (Pplus AND COMP) space to LDNMOS Drain MVSD (source and body tap butted). PCOMP (Pplus AND COMP) intercept with LDNMOS Drain MVSD is not allowed. is 0.92µm
+logger.info("Executing rule MDN.5aii")
+mdn5aii_l1 = pcomp.interacting(ncomp).inside(ldmos_xtor).inside(dualgate).not(nplus).separation(mvsd, 0.92.um, euclidian).polygons(0.001)
+mdn5aii_l1.output("MDN.5aii", "MDN.5aii : Min PCOMP (Pplus AND COMP) space to LDNMOS Drain MVSD (source and body tap butted). PCOMP (Pplus AND COMP) intercept with LDNMOS Drain MVSD is not allowed. : 0.92µm")
+mdn5aii_l1.forget
+
+ncomp_mdn5b = ncomp.inside(ldmos_xtor).inside(dualgate)
+pcomp_mdn5b = pcomp.inside(ldmos_xtor).inside(dualgate)
+# Rule MDN.5b: Min PCOMP (Pplus AND COMP) space to LDNMOS Source (Nplus AND COMP). Use butted source and p-substrate tab otherwise and that is good for Latch-up immunity as well.
+logger.info("Executing rule MDN.5b")
+mdn5b_l1 = ncomp_mdn5b.not(poly2).not(mvsd).separation(pcomp_mdn5b, 0.4.um, euclidian).polygons.or(ncomp_mdn5b.not(poly2).not(mvsd).and(pcomp_mdn5b))
+mdn5b_l1.output("MDN.5b", "MDN.5b : Min PCOMP (Pplus AND COMP) space to LDNMOS Source (Nplus AND COMP). Use butted source and p-substrate tab otherwise and that is good for Latch-up immunity as well.")
+mdn5b_l1.forget
+
+ncomp_mdn5b.forget
+pcomp_mdn5b.forget
+mdn_5c_ncompsd = ncomp.inside(ldmos_xtor).inside(dualgate).interacting(mvsd).sized(0.36.um).sized(-0.36.um).extents
+mdn_5c_error = mdn_5c_ncompsd.edges.centers(0, 0.99).not_interacting(mdn_5c_ncompsd.drc(separation(pcomp, euclidian) <= 15.um).polygons(0.001))
+# Rule MDN.5c: Maximum distance of the nearest edge of the substrate tab from NCOMP edge. is 15µm
+logger.info("Executing rule MDN.5c")
+mdn5c_l1 = mdn_5c_error.and(ncomp).and(pcomp.holes).extended(0, 0, 0.001, 0.001)
+mdn5c_l1.output("MDN.5c", "MDN.5c : Maximum distance of the nearest edge of the substrate tab from NCOMP edge. : 15µm")
+mdn5c_l1.forget
+
+mdn_5c_ncompsd.forget
+mdn_5c_error.forget
+# Rule MDN.6: ALL LDNMOS shall be covered by Dualgate layer.
+logger.info("Executing rule MDN.6")
+mdn6_l1 = ncomp.not(poly2).not(mvsd).or(ngate.not(mvsd)).or(ncomp.and(mvsd)).inside(ldmos_xtor).not_inside(dualgate)
+mdn6_l1.output("MDN.6", "MDN.6 : ALL LDNMOS shall be covered by Dualgate layer.")
+mdn6_l1.forget
+
+# Rule MDN.6a: Min Dualgate enclose NCOMP.
+logger.info("Executing rule MDN.6a")
+mdn6a_l1 = dualgate.enclosing(ncomp.inside(ldmos_xtor), 0.5.um, euclidian).polygons(0.001).or(ncomp.inside(ldmos_xtor).not_inside(dualgate))
+mdn6a_l1.output("MDN.6a", "MDN.6a : Min Dualgate enclose NCOMP.")
+mdn6a_l1.forget
+
+# Rule MDN.7: Each LDNMOS shall be covered by LDMOS_XTOR (GDS#226) mark layer.
+logger.info("Executing rule MDN.7")
+mdn7_l1 = ncomp.interacting(mvsd).not(poly2).not(mvsd).or(ngate.interacting(mvsd).not(mvsd)).or(ncomp.and(mvsd)).inside(dualgate).not_inside(ldmos_xtor)
+mdn7_l1.output("MDN.7", "MDN.7 : Each LDNMOS shall be covered by LDMOS_XTOR (GDS#226) mark layer.")
+mdn7_l1.forget
+
+# Rule MDN.7a: Min LDMOS_XTOR enclose Dualgate.
+logger.info("Executing rule MDN.7a")
+mdn7a_l1 = dualgate.not_outside(ldmos_xtor).not(ldmos_xtor).or(dualgate.interacting(mvsd).not_inside(ldmos_xtor))
+mdn7a_l1.output("MDN.7a", "MDN.7a : Min LDMOS_XTOR enclose Dualgate.")
+mdn7a_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_mdn_8a, unconnected_mdn_8b = conn_separation(mvsd, nwell, 1, 2, euclidian)
+
+# Rule MDN.8a: Min LDNMOS drain MVSD space to any other equal potential Nwell space.
+logger.info("Executing rule MDN.8a")
+mdn8a_l1 = connected_mdn_8a.or(mvsd.not_outside(nwell))
+mdn8a_l1.output("MDN.8a", "MDN.8a : Min LDNMOS drain MVSD space to any other equal potential Nwell space.")
+mdn8a_l1.forget
+
+# Rule MDN.8b: Min LDNMOS drain MVSD space to any other different potential Nwell space.
+logger.info("Executing rule MDN.8b")
+mdn8b_l1 = unconnected_mdn_8b.or(mvsd.not_outside(nwell))
+mdn8b_l1.output("MDN.8b", "MDN.8b : Min LDNMOS drain MVSD space to any other different potential Nwell space.")
+mdn8b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule MDN.8b: Min LDNMOS drain MVSD space to any other different potential Nwell space.
+logger.info("Executing rule MDN.8b")
+mdn8b_l1 = mvsd.separation(nwell, 2.um, euclidian).polygons(0.001).or(mvsd.not_outside(nwell))
+mdn8b_l1.output("MDN.8b", "MDN.8b : Min LDNMOS drain MVSD space to any other different potential Nwell space.")
+mdn8b_l1.forget
+
+end #CONNECTIVITY_RULES
+
+# Rule MDN.9: Min LDNMOS drain MVSD space to NCOMP (Nplus AND COMP) outside LDNMOS drain MVSD. is 4µm
+logger.info("Executing rule MDN.9")
+mdn9_l1 = mvsd.inside(dualgate).inside(ldmos_xtor).separation(ncomp.not_interacting(mvsd), 4.um, euclidian).polygons(0.001)
+mdn9_l1.output("MDN.9", "MDN.9 : Min LDNMOS drain MVSD space to NCOMP (Nplus AND COMP) outside LDNMOS drain MVSD. : 4µm")
+mdn9_l1.forget
+
+# rule MDN.10 is not a DRC check
+
+poly_mdn10 = poly2.inside(dualgate).inside(ldmos_xtor.interacting(mvsd))
+# Rule MDN.10a: Min LDNMOS POLY2 width. is 1.2µm
+logger.info("Executing rule MDN.10a")
+mdn10a_l1 = poly_mdn10.width(1.2.um, euclidian).polygons(0.001)
+mdn10a_l1.output("MDN.10a", "MDN.10a : Min LDNMOS POLY2 width. : 1.2µm")
+mdn10a_l1.forget
+
+# Rule MDN.10b: Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDNMOS drain direction). is 0.4µm
+logger.info("Executing rule MDN.10b")
+mdn10b_l1 = poly_mdn10.edges.enclosing(ncomp.interacting(poly_mdn10).edges.interacting(ncomp.edges.not_interacting(poly2)), 0.4.um, euclidian)
+mdn10b_l1.output("MDN.10b", "MDN.10b : Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDNMOS drain direction). : 0.4µm")
+mdn10b_l1.forget
+
+mdn_10c_all_errors = poly_mdn10.drc(enclosing(ncomp.interacting(poly_mdn10), euclidian) != 0.2.um)
+mdn_10c_error_region = ncomp.inside(dualgate).inside(ldmos_xtor).sized(0.36.um).sized(-0.36.um).extents.and(mvsd).and(poly2)
+# Rule MDN.10c: Min/Max POLY2 extension beyond COMP on the field towards LDNMOS drain COMP direction.
+logger.info("Executing rule MDN.10c")
+mdn10c_l1 = mdn_10c_all_errors.and(mdn_10c_error_region)
+mdn10c_l1.output("MDN.10c", "MDN.10c : Min/Max POLY2 extension beyond COMP on the field towards LDNMOS drain COMP direction.")
+mdn10c_l1.forget
+
+mdn_10c_all_errors.forget
+mdn_10c_error_region.forget
+mdn_10d_field = ncomp.and(poly2).sized(1.um, 0).and(poly2)
+mdn_10d_not_max = ncomp.inside(mvsd).inside(dualgate).inside(ldmos_xtor).drc(separation(mdn_10d_field) <= 0.16.um)
+mdn_10d_max = ncomp.sized(0.36.um).sized(-0.36.um).extents.not(mdn_10d_not_max.polygons).not(ncomp).not(poly2).inside(mvsd)
+mdn_10d_min = ncomp.inside(mvsd).inside(dualgate).inside(ldmos_xtor).separation(mdn_10d_field , 0.16.um).polygons(0.001)
+mdn_10d_overlap = ncomp.inside(mvsd).inside(dualgate).inside(ldmos_xtor).and(poly2)
+# Rule MDN.10d: Min/Max POLY2 on field space to LDNMOS drain COMP.
+logger.info("Executing rule MDN.10d")
+mdn10d_l1 = mdn_10d_max.or(mdn_10d_min).or(mdn_10d_overlap)
+mdn10d_l1.output("MDN.10d", "MDN.10d : Min/Max POLY2 on field space to LDNMOS drain COMP.")
+mdn10d_l1.forget
+
+mdn_10d_field.forget
+mdn_10d_not_max.forget
+mdn_10d_max.forget
+mdn_10d_min.forget
+mdn_10d_overlap.forget
+# Rule MDN.10ei: Min POLY2 space to Psub tap (source and body tap non-butted).
+logger.info("Executing rule MDN.10ei")
+mdn10ei_l1 = poly_mdn10.separation(pcomp.not_interacting(ncomp), 0.4.um).polygons(0.001).or(poly_mdn10.and(pcomp.not(nplus).not_interacting(ncomp.not(pplus))))
+mdn10ei_l1.output("MDN.10ei", "MDN.10ei : Min POLY2 space to Psub tap (source and body tap non-butted).")
+mdn10ei_l1.forget
+
+# Rule MDN.10eii: Min POLY2 space to Psub tap (source and body tap butted). is 0.32µm
+logger.info("Executing rule MDN.10eii")
+mdn10eii_l1 = poly_mdn10.separation(pcomp.not(nplus).interacting(ncomp.not(pplus)), 0.32.um, euclidian).polygons(0.001)
+mdn10eii_l1.output("MDN.10eii", "MDN.10eii : Min POLY2 space to Psub tap (source and body tap butted). : 0.32µm")
+mdn10eii_l1.forget
+
+# Rule MDN.10f: Poly2 interconnect in HV region (LDMOS_XTOR marked region) not allowed. Also, any Poly2 interconnect with poly2 to substrate potential greater than 6V is not allowed.
+logger.info("Executing rule MDN.10f")
+mdn10f_l1 = poly_mdn10.not(nplus).interacting(poly_mdn10.and(nplus),2).or(poly2.and(ldmos_xtor).interacting(poly2.not(ldmos_xtor)))
+mdn10f_l1.output("MDN.10f", "MDN.10f : Poly2 interconnect in HV region (LDMOS_XTOR marked region) not allowed. Also, any Poly2 interconnect with poly2 to substrate potential greater than 6V is not allowed.")
+mdn10f_l1.forget
+
+poly_mdn10.forget
+mdn_11_layer = ldmos_xtor.and(mvsd).and(comp).and(poly2).and(nplus)
+mdn_11_max = mdn_11_layer.not(mdn_11_layer.drc(width <= 0.4.um).polygons)
+mdn_11_min = mdn_11_layer.width(0.4.um).polygons(0.001).not_interacting(mdn_11_max)
+mdn_11_no_channel = mvsd.covering(ncomp).outside(tgate).inside(dualgate).inside(ldmos_xtor).or(mvsd.not_covering(ncomp.not_interacting(poly2)).inside(dualgate).inside(ldmos_xtor))
+# Rule MDN.11: Min/Max MVSD overlap channel COMP ((((LDMOS_XTOR AND MVSD) AND COMP) AND POLY2) AND NPlus).
+logger.info("Executing rule MDN.11")
+mdn11_l1 = mdn_11_max.or(mdn_11_min).or(mdn_11_no_channel)
+mdn11_l1.output("MDN.11", "MDN.11 : Min/Max MVSD overlap channel COMP ((((LDMOS_XTOR AND MVSD) AND COMP) AND POLY2) AND NPlus).")
+mdn11_l1.forget
+
+mdn_11_layer.forget
+mdn_11_max.forget
+mdn_11_min.forget
+mdn_11_no_channel.forget
+mdn12_a = mvsd.covering(ncomp.not_interacting(poly2)).enclosing(ncomp, 0.5.um, transparent).polygons(0.001).outside(poly2).inside(dualgate).inside(ldmos_xtor)
+mdn12_b = mvsd.not_covering(ncomp.not_interacting(poly2)).inside(dualgate).inside(ldmos_xtor)
+# Rule MDN.12: Min MVSD enclose NCOMP in the LDNMOS drain and in the direction along the transistor width.
+logger.info("Executing rule MDN.12")
+mdn12_l1 = mdn12_a.or(mdn12_b)
+mdn12_l1.output("MDN.12", "MDN.12 : Min MVSD enclose NCOMP in the LDNMOS drain and in the direction along the transistor width.")
+mdn12_l1.forget
+
+mdn12_a.forget
+mdn12_b.forget
+# rule MDN.13 is not a DRC check
+
+# Rule MDN.13a: Max single finger width. is 50µm
+logger.info("Executing rule MDN.13a")
+mdn13a_l1 = poly2.and(ncomp).not(mvsd).inside(dualgate).inside(ldmos_xtor).drc(length > 50.um)
+mdn13a_l1.output("MDN.13a", "MDN.13a : Max single finger width. : 50µm")
+mdn13a_l1.forget
+
+mdn_source = ncomp.interacting(poly2.and(dualgate).and(ldmos_xtor).and(mvsd)).not(poly2)
+mdn_ldnmos = poly2.and(ncomp).and(dualgate).not(mvsd).inside(ldmos_xtor)
+# Rule MDN.13b: Layout shall have alternative source & drain.
+logger.info("Executing rule MDN.13b")
+mdn13b_l1 = mdn_ldnmos.not_interacting(mdn_source,1,1).or(mdn_ldnmos.not_interacting(mvsd,1,1)).or(mdn_source.interacting(mvsd))
+mdn13b_l1.output("MDN.13b", "MDN.13b : Layout shall have alternative source & drain.")
+mdn13b_l1.forget
+
+mdn_13c_source_side = mdn_ldnmos.interacting(mdn_source.interacting(mdn_ldnmos, 2, 2).or(mdn_source.interacting(pcomp.interacting(mdn_source, 2, 2))))
+# Rule MDN.13c: Both sides of the transistor shall be terminated by source.
+logger.info("Executing rule MDN.13c")
+mdn13c_l1 = mvsd.covering(ncomp.not_interacting(poly2)).interacting(ncomp, 2, 2).interacting(mdn_13c_source_side)
+mdn13c_l1.output("MDN.13c", "MDN.13c : Both sides of the transistor shall be terminated by source.")
+mdn13c_l1.forget
+
+mdn_13c_source_side.forget
+mdn_13d_single = mvsd.covering(ncomp.not_interacting(poly2)).interacting(ncomp, 2, 2).inside(ldmos_xtor)
+mdn_13d_multi = mvsd.covering(ncomp.not_interacting(poly2)).interacting(ncomp, 3, 3).inside(ldmos_xtor)
+mdn_13d_butted_well = mdn_source.sized(1.um).sized(-1.um).extents.not(pcomp).interacting(mdn_ldnmos,2,2)
+# Rule MDN.13d: Every two poly fingers shall be surrounded by a P-sub guard ring. (Exclude the case when each LDNMOS transistor have full width butting to well tap).
+logger.info("Executing rule MDN.13d")
+mdn13d_l1 = pcomp.holes.covering(mdn_13d_single, 2).or(pcomp.holes.covering(mdn_13d_single).covering(mdn_13d_multi)).or(mdn_13d_butted_well)
+mdn13d_l1.output("MDN.13d", "MDN.13d : Every two poly fingers shall be surrounded by a P-sub guard ring. (Exclude the case when each LDNMOS transistor have full width butting to well tap).")
+mdn13d_l1.forget
+
+mdn_13d_single.forget
+mdn_13d_multi.forget
+mdn_13d_butted_well.forget
+mdn_source.forget
+mdn_ldnmos.forget
+# Rule MDN.14: Min MVSD space to any DNWELL.
+logger.info("Executing rule MDN.14")
+mdn14_l1 = mvsd.separation(dnwell,6.0.um).polygons(0.001).or(mvsd.not_outside(dnwell))
+mdn14_l1.output("MDN.14", "MDN.14 : Min MVSD space to any DNWELL.")
+mdn14_l1.forget
+
+# Rule MDN.15a: Min LDNMOS drain COMP width. is 0.22µm
+logger.info("Executing rule MDN.15a")
+mdn15a_l1 = comp.inside(mvsd).inside(dualgate).inside(ldmos_xtor).width(0.22.um, euclidian).polygons(0.001)
+mdn15a_l1.output("MDN.15a", "MDN.15a : Min LDNMOS drain COMP width. : 0.22µm")
+mdn15a_l1.forget
+
+# Rule MDN.15b: Min LDNMOS drain COMP enclose contact. is 0µm
+logger.info("Executing rule MDN.15b")
+mdn15b_l1 = contact.interacting(ncomp.inside(mvsd).inside(dualgate).inside(ldmos_xtor)).not_inside(ncomp.inside(mvsd))
+mdn15b_l1.output("MDN.15b", "MDN.15b : Min LDNMOS drain COMP enclose contact. : 0µm")
+mdn15b_l1.forget
+
+# rule MDN.16 is not a DRC check
+
+mdn_17_blockages = pcomp.holes.not(ncomp.or(poly2).interacting(mvsd)).covering(dnwell.or(nwell)).inside(dualgate).inside(ldmos_xtor.interacting(mvsd))
+mdn_17_mos_in_gr = ngate.not(mvsd).not_inside(pcomp.holes).inside(dualgate).inside(ldmos_xtor.interacting(mvsd))
+mdn_17_gr_in_ldmos_mk = ldmos_xtor.interacting(mvsd).and(dualgate).not_covering(pcomp)
+# Rule MDN.17: It is recommended to surround the LDNMOS transistor with non-broken Psub guard ring to improve the latch up immunity. Guideline to improve the latch up immunity.
+logger.info("Executing rule MDN.17")
+mdn17_l1 = mdn_17_blockages.or(mdn_17_mos_in_gr).or(mdn_17_gr_in_ldmos_mk)
+mdn17_l1.output("MDN.17", "MDN.17 : It is recommended to surround the LDNMOS transistor with non-broken Psub guard ring to improve the latch up immunity. Guideline to improve the latch up immunity.")
+mdn17_l1.forget
+
+mdn_17_blockages.forget
+mdn_17_mos_in_gr.forget
+mdn_17_gr_in_ldmos_mk.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/10v_ldpmos.drc b/rules/klayout/drc/rule_decks/10v_ldpmos.drc
new file mode 100644
index 0000000..5096d24
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/10v_ldpmos.drc
@@ -0,0 +1,612 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#--------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (10V LDPMOS) ----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+metal1 = polygons(34 , 0 )
+mvpsd = polygons(11 , 39)
+ldmos_xtor = polygons(226, 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+#================================================
+#------------- LAYERS CONNECTIONS ---------------
+#================================================
+
+if CONNECTIVITY_RULES
+
+ logger.info("Construct connectivity for the design.")
+
+ connect(dnwell, ncomp)
+ connect(ncomp, contact)
+ connect(pcomp, contact)
+ connect(lvpwell, ncomp)
+ connect(nwell, ncomp)
+ connect(natcompsd, contact)
+ connect(mvsd, ncomp)
+ connect(mvpsd, pcomp)
+ connect(contact, metal1)
+ connect(metal1, via1)
+ connect(via1, metal2)
+ connect(metal2, via2)
+ connect(via2, metal3)
+ connect(metal3, via3)
+ connect(via3, metal4)
+ connect(metal4, via4)
+ connect(via4, metal5)
+ connect(metal5, via5)
+ connect(via5, metaltop)
+
+end #CONNECTIVITY_RULES
+
+#================================================
+#------------ PRE-DEFINED FUNCTIONS -------------
+#================================================
+
+def conn_space(layer,conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer.space(conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
+ singularity_errors = layer.space(0.001.um)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
+ return connected_output, unconnected_output
+end
+
+def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons(0.001)
+ return connected_output, unconnected_output
+end
+
+# === IMPLICIT EXTRACTION ===
+if CONNECTIVITY_RULES
+ logger.info("Connectivity rules enabled, Netlist object will be generated.")
+ netlist
+end #CONNECTIVITY_RULES
+
+# === LAYOUT EXTENT ===
+CHIP = extent.sized(0.0)
+
+logger.info("Total area of the design is #{CHIP.area()} um^2.")
+
+
+
+#================================================
+#-------------------10V LDPMOS-------------------
+#================================================
+
+mdp_source = (pcomp).interacting(poly2.and(dualgate).and(ldmos_xtor).and(mvpsd)).not(poly2)
+ldpmos = poly2.and(pcomp).and(dualgate).not(mvpsd).inside(ldmos_xtor)
+# Rule MDP.1: Minimum transistor channel length. is 0.6µm
+logger.info("Executing rule MDP.1")
+mdp1_l1 = poly2.and(comp).inside(ldmos_xtor).inside(dualgate).enclosing(mvpsd, 0.6.um, euclidian).polygons(0.001)
+mdp1_l1.output("MDP.1", "MDP.1 : Minimum transistor channel length. : 0.6µm")
+mdp1_l1.forget
+
+mvpsd_mdp = mvpsd.edges.and(pcomp).and(poly2)
+# Rule MDP.1a: Max transistor channel length.
+logger.info("Executing rule MDP.1a")
+mdp1a_l1 = poly2.edges.and(pcomp).or(mvpsd_mdp).and(ldmos_xtor).and(dualgate).not(pgate.not(mvpsd).edges.interacting(poly2.edges.and(pcomp).or(mvpsd_mdp)).width(20.001.um).edges)
+mdp1a_l1.output("MDP.1a", "MDP.1a : Max transistor channel length.")
+mdp1a_l1.forget
+
+mvpsd_mdp.forget
+# Rule MDP.2: Minimum transistor channel width. is 4µm
+logger.info("Executing rule MDP.2")
+mdp2_l1 = poly2.and(comp).inside(ldmos_xtor).inside(dualgate).edges.not(mvpsd).interacting(mvpsd).width(4.um, euclidian).polygons(0.001)
+mdp2_l1.output("MDP.2", "MDP.2 : Minimum transistor channel width. : 4µm")
+mdp2_l1.forget
+
+mdp3_1 = ldpmos.or(mvpsd).or(mdp_source).not_interacting(ncomp.holes).inside(dualgate).inside(ldmos_xtor)
+mdp3_2 = ncomp.holes.not_interacting(ncomp.interacting(mdp_source)).not_interacting(mvpsd,1,1).inside(dualgate).inside(ldmos_xtor)
+# Rule MDP.3: Each LDPMOS shall be surrounded by non-broken Nplus guard ring inside DNWELL
+logger.info("Executing rule MDP.3")
+mdp3_l1 = mdp3_1.or(mdp3_2)
+mdp3_l1.output("MDP.3", "MDP.3 : Each LDPMOS shall be surrounded by non-broken Nplus guard ring inside DNWELL")
+mdp3_l1.forget
+
+ncomp_mdp3ai = ncomp.not_interacting(pcomp).inside(ldmos_xtor).inside(dualgate)
+# Rule MDP.3ai: Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap non-butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.
+logger.info("Executing rule MDP.3ai")
+mdp3ai_l1 = ncomp_mdp3ai.separation(mvpsd, 1.um, euclidian).polygons(0.001).or(mvpsd.interacting(ncomp_mdp3ai))
+mdp3ai_l1.output("MDP.3ai", "MDP.3ai : Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap non-butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.")
+mdp3ai_l1.forget
+
+ncomp_mdp3ai.forget
+ncomp_mdp3aii = ncomp.interacting(pcomp).inside(ldmos_xtor).inside(dualgate)
+# Rule MDP.3aii: Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.
+logger.info("Executing rule MDP.3aii")
+mdp3aii_l1 = ncomp_mdp3aii.separation(mvpsd, 0.92.um, euclidian).polygons(0.001).or(mvpsd.interacting(ncomp_mdp3aii))
+mdp3aii_l1.output("MDP.3aii", "MDP.3aii : Min NCOMP (Nplus AND COMP) space to MVPSD (source and body tap butted). NCOMP (Nplus AND COMP) intercept with MVPSD is not allowed.")
+mdp3aii_l1.forget
+
+ncomp_mdp3aii.forget
+ncomp_mdp3b = ncomp.inside(ldmos_xtor).inside(dualgate)
+pcomp_mdp3b = pcomp.inside(dnwell).inside(ldmos_xtor).inside(dualgate)
+# Rule MDP.3b: Min NCOMP (Nplus AND COMP) space to PCOMP in DNWELL (Pplus AND COMP AND DNWELL). Use butted source and DNWELL contacts otherwise and that is best for Latch-up immunity as well. is 0.4µm
+logger.info("Executing rule MDP.3b")
+mdp3b_l1 = ncomp_mdp3b.not(poly2).not(mvpsd).separation(pcomp_mdp3b.not(poly2).not(mvpsd), 0.4.um, euclidian).polygons(0.001)
+mdp3b_l1.output("MDP.3b", "MDP.3b : Min NCOMP (Nplus AND COMP) space to PCOMP in DNWELL (Pplus AND COMP AND DNWELL). Use butted source and DNWELL contacts otherwise and that is best for Latch-up immunity as well. : 0.4µm")
+mdp3b_l1.forget
+
+ncomp_mdp3b.forget
+pcomp_mdp3b.forget
+# Rule MDP.3c: Maximum distance of the nearest edge of the DNWELL tab (NCOMP inside DNWELL) from PCOMP edge (PCOMP inside DNWELL). is 15µm
+logger.info("Executing rule MDP.3c")
+mdp3c_l1 = ncomp.inside(dnwell).inside(ldmos_xtor).inside(dualgate).not_interacting(ncomp.inside(dnwell).drc(separation(pcomp.inside(dnwell)) <= 15.um).first_edges,4)
+mdp3c_l1.output("MDP.3c", "MDP.3c : Maximum distance of the nearest edge of the DNWELL tab (NCOMP inside DNWELL) from PCOMP edge (PCOMP inside DNWELL). : 15µm")
+mdp3c_l1.forget
+
+# Rule MDP.3d: The metal connection for the Nplus guard ring recommended to be continuous. The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability. is 10µm
+logger.info("Executing rule MDP.3d")
+mdp3d_l1 = ncomp.interacting(ldmos_xtor.interacting(mvpsd)).interacting(dualgate).not(metal1).edges.not(metal1).with_length(10.001.um, nil)
+mdp3d_l1.output("MDP.3d", "MDP.3d : The metal connection for the Nplus guard ring recommended to be continuous. The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability. : 10µm")
+mdp3d_l1.forget
+
+mdp4_metal = pcomp.not_interacting(mvpsd).interacting(ldmos_xtor.interacting(mvpsd)).interacting(dualgate).not(metal1).edges.not(metal1).with_length(10.001.um, nil)
+# Rule MDP.4: DNWELL covering LDPMOS shall be surrounded by non broken Pplus guard. The metal connection for the Pplus guard ring recommended to be continuous, The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability.
+logger.info("Executing rule MDP.4")
+mdp4_l1 = pcomp.interacting(metal1).not_interacting(pcomp.holes).edges.and(ldmos_xtor).and(dualgate).or(mdp4_metal)
+mdp4_l1.output("MDP.4", "MDP.4 : DNWELL covering LDPMOS shall be surrounded by non broken Pplus guard. The metal connection for the Pplus guard ring recommended to be continuous, The maximum gap between this metal if broken. Note: To put maximum number of contact under metal for better manufacturability and reliability.")
+mdp4_l1.forget
+
+mdp4_metal.forget
+# Rule MDP.4a: Min PCOMP (Pplus AND COMP) space to DNWELL. is 2.5µm
+logger.info("Executing rule MDP.4a")
+mdp4a_l1 = pcomp.inside(ldmos_xtor).inside(dualgate).separation(dnwell.inside(ldmos_xtor).inside(dualgate), 2.5.um, euclidian).polygons(0.001)
+mdp4a_l1.output("MDP.4a", "MDP.4a : Min PCOMP (Pplus AND COMP) space to DNWELL. : 2.5µm")
+mdp4a_l1.forget
+
+mdp4b_dnwell_edges = dnwell.inside(ldmos_xtor).inside(dualgate).edges.centers(0, 0.99)
+mdp4b_not_error = dnwell.drc(separation(pcomp.inside(ldmos_xtor.interacting(mvpsd)).inside(dualgate).not_interacting(mvpsd), euclidian) <= 15.um).polygons(0.001)
+# Rule MDP.4b: Maximum distance of the nearest edge of the DNWELL from the PCOMP Guard ring outside DNWELL. is 15µm
+logger.info("Executing rule MDP.4b")
+mdp4b_l1 = mdp4b_dnwell_edges.not_interacting(mdp4b_not_error).and(pcomp.holes).extended(0, 0, 0.001, 0.001)
+mdp4b_l1.output("MDP.4b", "MDP.4b : Maximum distance of the nearest edge of the DNWELL from the PCOMP Guard ring outside DNWELL. : 15µm")
+mdp4b_l1.forget
+
+mdp4b_dnwell_edges.forget
+mdp4b_not_error.forget
+# Rule MDP.5: Each LDPMOS shall be covered by Dualgate layer.
+logger.info("Executing rule MDP.5")
+mdp5_l1 = pcomp.not(poly2).not(mvpsd).or(pgate.not(mvpsd)).or(pcomp.and(mvpsd)).inside(ldmos_xtor).not_inside(dualgate)
+mdp5_l1.output("MDP.5", "MDP.5 : Each LDPMOS shall be covered by Dualgate layer.")
+mdp5_l1.forget
+
+# Rule MDP.5a: Minimum Dualgate enclose Plus guarding ring PCOMP (Pplus AND COMP). is 0.5µm
+logger.info("Executing rule MDP.5a")
+mdp5a_l1 = dualgate.interacting(ldmos_xtor).enclosing(pcomp.inside(ldmos_xtor), 0.5.um, euclidian).polygons(0.001)
+mdp5a_l2 = pcomp.inside(ldmos_xtor).not_outside(dualgate.interacting(ldmos_xtor)).not(dualgate.interacting(ldmos_xtor))
+mdp5a_l = mdp5a_l1.or(mdp5a_l2)
+mdp5a_l.output("MDP.5a", "MDP.5a : Minimum Dualgate enclose Plus guarding ring PCOMP (Pplus AND COMP). : 0.5µm")
+mdp5a_l1.forget
+mdp5a_l2.forget
+mdp5a_l.forget
+
+# Rule MDP.6: Each LDPMOS shall be covered by LDMOS_XTOR (GDS#226) layer.
+logger.info("Executing rule MDP.6")
+mdp6_l1 = mvpsd.not_inside(ldmos_xtor)
+mdp6_l1.output("MDP.6", "MDP.6 : Each LDPMOS shall be covered by LDMOS_XTOR (GDS#226) layer.")
+mdp6_l1.forget
+
+# Rule MDP.6a: Minimum LDMOS_XTOR enclose Dualgate.
+logger.info("Executing rule MDP.6a")
+mdp6a_l1 = ldmos_xtor.not_covering(dualgate)
+mdp6a_l1.output("MDP.6a", "MDP.6a : Minimum LDMOS_XTOR enclose Dualgate.")
+mdp6a_l1.forget
+
+# Rule MDP.7: Minimum LDMOS_XTOR layer space to Nwell outside LDMOS_XTOR. is 2µm
+logger.info("Executing rule MDP.7")
+mdp7_l1 = ldmos_xtor.separation(nwell.outside(ldmos_xtor), 2.um, euclidian).polygons(0.001)
+mdp7_l1.output("MDP.7", "MDP.7 : Minimum LDMOS_XTOR layer space to Nwell outside LDMOS_XTOR. : 2µm")
+mdp7_l1.forget
+
+# Rule MDP.8: Minimum LDMOS_XTOR layer space to NCOMP outside LDMOS_XTOR. is 1.5µm
+logger.info("Executing rule MDP.8")
+mdp8_l1 = ldmos_xtor.separation(ncomp.outside(ldmos_xtor), 1.5.um, euclidian).polygons(0.001)
+mdp8_l1.output("MDP.8", "MDP.8 : Minimum LDMOS_XTOR layer space to NCOMP outside LDMOS_XTOR. : 1.5µm")
+mdp8_l1.forget
+
+# Rule MDP.9a: Min LDPMOS POLY2 width. is 1.2µm
+logger.info("Executing rule MDP.9a")
+mdp9a_l1 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).width(1.2.um, euclidian).polygons(0.001)
+mdp9a_l1.output("MDP.9a", "MDP.9a : Min LDPMOS POLY2 width. : 1.2µm")
+mdp9a_l1.forget
+
+mdp9b_1 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.interacting(mvpsd).not(mvpsd).enclosing(comp.edges,0.4.um).edges
+mdp9b_2 = poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.interacting(mvpsd).not(mvpsd).interacting(pcomp)
+# Rule MDP.9b: Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDMOS drain direction). is 0.4µm
+logger.info("Executing rule MDP.9b")
+mdp9b_l1 = mdp9b_1.or(mdp9b_2).extended(0,0,0.001,0.001)
+mdp9b_l1.output("MDP.9b", "MDP.9b : Min POLY2 extension beyond COMP in the width direction of the transistor (other than the LDMOS drain direction). : 0.4µm")
+mdp9b_l1.forget
+
+mdp9b_1.forget
+mdp9b_2.forget
+# Rule MDP.9c: Min/Max POLY2 extension beyond COMP on the field towards LDPMOS drain (MVPSD AND COMP AND Pplus NOT POLY2) direction.
+logger.info("Executing rule MDP.9c")
+mdp9c_l1 = poly2.edges.in(poly2.inside(dnwell.and(dualgate).and(ldmos_xtor)).edges.inside_part(mvpsd)).not_interacting(poly2.drc(enclosing(comp,projection) == 0.2.um))
+mdp9c_l1.output("MDP.9c", "MDP.9c : Min/Max POLY2 extension beyond COMP on the field towards LDPMOS drain (MVPSD AND COMP AND Pplus NOT POLY2) direction.")
+mdp9c_l1.forget
+
+# Rule MDP.9d: Min/Max POLY2 on field to LDPMOS drain COMP (MVPSD AND COMP AND Pplus NOT POLY2) space.
+logger.info("Executing rule MDP.9d")
+mdp9d_l1 = poly2.inside(dualgate).inside(ldmos_xtor).overlapping(mvpsd.and(pcomp).not(poly2).sized(0.16.um)).or(poly2.inside(dualgate).inside(ldmos_xtor.interacting(mvpsd)).not_interacting(mvpsd.and(pcomp).not(poly2).sized(0.16.um)))
+mdp9d_l1.output("MDP.9d", "MDP.9d : Min/Max POLY2 on field to LDPMOS drain COMP (MVPSD AND COMP AND Pplus NOT POLY2) space.")
+mdp9d_l1.forget
+
+ldpmos_poly2_gate = poly2.interacting(pgate.and(dualgate).not(mvpsd))
+ncomp_not_butted = ncomp.not(pplus).not_interacting(pcomp.not(nplus)).or(ncomp.not(pplus).overlapping(pcomp.not(nplus)))
+mdp9ei_1 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).separation(ncomp_not_butted, 0.4.um).polygons(0.001)
+mdp9ei_2 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).and(ncomp_not_butted)
+# Rule MDP.9ei: Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap non-butted).
+logger.info("Executing rule MDP.9ei")
+mdp9ei_l1 = mdp9ei_1.or(mdp9ei_2)
+mdp9ei_l1.output("MDP.9ei", "MDP.9ei : Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap non-butted).")
+mdp9ei_l1.forget
+
+ncomp_not_butted.forget
+mdp9ei_1.forget
+mdp9ei_2.forget
+ncomp_butted = ncomp.not(pplus).interacting(pcomp.not(nplus)).not_overlapping(pcomp.not(nplus))
+mdp9eii_1 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).separation(ncomp_butted, 0.32.um).polygons(0.001)
+mdp9eii_2 = ldpmos_poly2_gate.inside(dualgate).inside(ldmos_xtor).and(ncomp_butted)
+# Rule MDP.9eii: Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap butted).
+logger.info("Executing rule MDP.9eii")
+mdp9eii_l1 = mdp9eii_1.or(mdp9eii_2)
+mdp9eii_l1.output("MDP.9eii", "MDP.9eii : Min LDMPOS gate Poly2 space to Nplus guardring (source and body tap butted).")
+mdp9eii_l1.forget
+
+ncomp_butted.forget
+mdp9eii_1.forget
+mdp9eii_2.forget
+# Rule MDP.9f: Poly2 interconnect is not allowed in LDPMOS region (LDMOS_XTOR marked region). is -µm
+logger.info("Executing rule MDP.9f")
+mdp9f_l1 = poly2.not(pplus).inside(dualgate).inside(ldmos_xtor).interacting(poly2.and(pplus).inside(dualgate).inside(ldmos_xtor),2)
+mdp9f_l1.output("MDP.9f", "MDP.9f : Poly2 interconnect is not allowed in LDPMOS region (LDMOS_XTOR marked region). : -µm")
+mdp9f_l1.forget
+
+# Rule MDP.10: Min/Max MVPSD overlap onto the channel (LDMOS_XTOR AND COMP AND POLY2 AND Pplus).
+logger.info("Executing rule MDP.10")
+mdp10_l1 = mvpsd.inside(dualgate).inside(ldmos_xtor).not_interacting(mvpsd.drc(overlap(ldmos_xtor.and(comp).and(poly2).and(pplus),projection) == 0.4))
+mdp10_l1.output("MDP.10", "MDP.10 : Min/Max MVPSD overlap onto the channel (LDMOS_XTOR AND COMP AND POLY2 AND Pplus).")
+mdp10_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_mdp_10b, unconnected_mdp_10a = conn_space(mvpsd, 1, 2, euclidian)
+
+# Rule MDP.10a: Min MVPSD space within LDMOS_XTOR marking [diff potential]. is 2µm
+logger.info("Executing rule MDP.10a")
+mdp10a_l1 = unconnected_mdp_10a
+mdp10a_l1.output("MDP.10a", "MDP.10a : Min MVPSD space within LDMOS_XTOR marking [diff potential]. : 2µm")
+mdp10a_l1.forget
+
+# Rule MDP.10b: Min MVPSD space [same potential]. Merge if space less than 1um. is 1µm
+logger.info("Executing rule MDP.10b")
+mdp10b_l1 = connected_mdp_10b
+mdp10b_l1.output("MDP.10b", "MDP.10b : Min MVPSD space [same potential]. Merge if space less than 1um. : 1µm")
+mdp10b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule MDP.10a: Min MVPSD space within LDMOS_XTOR marking [diff potential]. is 2µm
+logger.info("Executing rule MDP.10a")
+mdp10a_l1 = mvpsd.space(2.um, euclidian).polygons(0.001)
+mdp10a_l1.output("MDP.10a", "MDP.10a : Min MVPSD space within LDMOS_XTOR marking [diff potential]. : 2µm")
+mdp10a_l1.forget
+
+end #CONNECTIVITY_RULES
+
+# Rule MDP.11: Min MVPSD enclosing PCOMP in the drain (MVPSD AND COMP NOT POLY2) direction and in the direction along the transistor width.
+logger.info("Executing rule MDP.11")
+mdp11_l1 = mvpsd.edges.not_interacting(pcomp.edges).enclosing(pcomp.edges, 0.8.um, euclidian).polygons(0.001).or(mvpsd.interacting(mvpsd.edges.and(pcomp.edges)))
+mdp11_l1.output("MDP.11", "MDP.11 : Min MVPSD enclosing PCOMP in the drain (MVPSD AND COMP NOT POLY2) direction and in the direction along the transistor width.")
+mdp11_l1.forget
+
+# Rule MDP.12: Min DNWELL enclose Nplus guard ring (NCOMP). is 0.66µm
+logger.info("Executing rule MDP.12")
+mdp12_l1 = dnwell.inside(dualgate).inside(ldmos_xtor).enclosing(ncomp.inside(dualgate).inside(ldmos_xtor), 0.66.um, euclidian).polygons(0.001)
+mdp12_l2 = ncomp.inside(dualgate).inside(ldmos_xtor).not_outside(dnwell.inside(dualgate).inside(ldmos_xtor)).not(dnwell.inside(dualgate).inside(ldmos_xtor))
+mdp12_l = mdp12_l1.or(mdp12_l2)
+mdp12_l.output("MDP.12", "MDP.12 : Min DNWELL enclose Nplus guard ring (NCOMP). : 0.66µm")
+mdp12_l1.forget
+mdp12_l2.forget
+mdp12_l.forget
+
+# rule MDP.13 is not a DRC check
+
+# Rule MDP.13a: Max single finger width. is 50µm
+logger.info("Executing rule MDP.13a")
+mdp13a_l1 = poly2.and(pcomp).not(mvpsd).inside(dualgate).inside(ldmos_xtor).edges.with_length(50.001.um,nil).extended(0, 0, 0.001, 0.001)
+mdp13a_l1.output("MDP.13a", "MDP.13a : Max single finger width. : 50µm")
+mdp13a_l1.forget
+
+# Rule MDP.13b: Layout shall have alternative source & drain.
+logger.info("Executing rule MDP.13b")
+mdp13b_l1 = ldpmos.not_interacting(mdp_source,1,1).or(ldpmos.not_interacting(mvpsd,1,1)).or(mdp_source.interacting(mvpsd))
+mdp13b_l1.output("MDP.13b", "MDP.13b : Layout shall have alternative source & drain.")
+mdp13b_l1.forget
+
+mdp_13c_source_side = ldpmos.interacting(mdp_source.interacting(ldpmos, 2, 2).or(mdp_source.interacting(ncomp.interacting(mdp_source, 2, 2))))
+# Rule MDP.13c: Both sides of the transistor shall be terminated by source.
+logger.info("Executing rule MDP.13c")
+mdp13c_l1 = mvpsd.covering(pcomp.not_interacting(poly2)).interacting(pcomp, 2, 2).interacting(mdp_13c_source_side)
+mdp13c_l1.output("MDP.13c", "MDP.13c : Both sides of the transistor shall be terminated by source.")
+mdp13c_l1.forget
+
+mdp_13c_source_side.forget
+# rule MDP.14 is not a DRC check
+
+# Rule MDP.15: Min DNWELL enclosing MVPSD to any DNWELL spacing. is 6µm
+logger.info("Executing rule MDP.15")
+mdp15_l1 = dnwell.separation(dnwell.covering(mvpsd).inside(dualgate).inside(ldmos_xtor), 6.um, euclidian).polygons(0.001)
+mdp15_l1.output("MDP.15", "MDP.15 : Min DNWELL enclosing MVPSD to any DNWELL spacing. : 6µm")
+mdp15_l1.forget
+
+# Rule MDP.16a: Min LDPMOS drain COMP width. is 0.22µm
+logger.info("Executing rule MDP.16a")
+mdp16a_l1 = comp.inside(mvpsd).inside(dualgate).inside(ldmos_xtor).width(0.22.um, euclidian).polygons(0.001)
+mdp16a_l1.output("MDP.16a", "MDP.16a : Min LDPMOS drain COMP width. : 0.22µm")
+mdp16a_l1.forget
+
+# Rule MDP.16b: Min LDPMOS drain COMP enclose contact. is 0µm
+logger.info("Executing rule MDP.16b")
+mdp16b_l1 = contact.interacting(pcomp.inside(mvpsd).inside(dualgate).inside(ldmos_xtor)).not_inside(pcomp.inside(mvpsd))
+mdp16b_l1.output("MDP.16b", "MDP.16b : Min LDPMOS drain COMP enclose contact. : 0µm")
+mdp16b_l1.forget
+
+mdp17_a1 = mvpsd.inside(dnwell).inside(ldmos_xtor)
+mdp17_a2 = ncomp.outside(dnwell).outside(nwell)
+# Rule MDP.17a: For better latch up immunity, it is necessary to put DNWELL guard ring between MVPSD Inside DNWELL covered by LDMOS_XTOR and NCOMP (outside DNWELL and outside Nwell) when spacing between them is less than 40um.
+logger.info("Executing rule MDP.17a")
+mdp17a_l1 = mdp17_a1.separation(mdp17_a2,transparent,40.um).polygons(0.001).not_interacting(ncomp.and(dnwell).holes)
+mdp17a_l1.output("MDP.17a", "MDP.17a : For better latch up immunity, it is necessary to put DNWELL guard ring between MVPSD Inside DNWELL covered by LDMOS_XTOR and NCOMP (outside DNWELL and outside Nwell) when spacing between them is less than 40um.")
+mdp17a_l1.forget
+
+mdp17_a1.forget
+mdp17_a2.forget
+# Rule MDP.17c: DNWELL guard ring shall have NCOMP tab to be connected to highest potential
+logger.info("Executing rule MDP.17c")
+mdp17c_l1 = dnwell.with_holes.not_covering(ncomp)
+mdp17c_l1.output("MDP.17c", "MDP.17c : DNWELL guard ring shall have NCOMP tab to be connected to highest potential")
+mdp17c_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/3.3v_sram.drc b/rules/klayout/drc/rule_decks/3.3v_sram.drc
new file mode 100644
index 0000000..db61047
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/3.3v_sram.drc
@@ -0,0 +1,268 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#---------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (3.3V SRAM) ----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+contact = polygons(33 , 0 )
+metal1 = polygons(34 , 0 )
+sramcore = polygons(108, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#-------------------3.3V SRAM--------------------
+#================================================
+
+# Rule S.DF.4c_LV: Min. (Nwell overlap of PCOMP) outside DNWELL. is 0.4µm
+logger.info("Executing rule S.DF.4c_LV")
+sdf4c_l1 = nwell.outside(dnwell).inside(sramcore).enclosing(pcomp.outside(dnwell).inside(sramcore), 0.4.um, euclidian).polygons(0.001)
+sdf4c_l2 = pcomp.outside(dnwell).inside(sramcore).not_outside(nwell.outside(dnwell).inside(sramcore)).not(nwell.outside(dnwell).inside(sramcore))
+sdf4c_l = sdf4c_l1.or(sdf4c_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+sdf4c_l.output("S.DF.4c_LV", "S.DF.4c_LV : Min. (Nwell overlap of PCOMP) outside DNWELL. : 0.4µm")
+sdf4c_l1.forget
+sdf4c_l2.forget
+sdf4c_l.forget
+
+# Rule S.DF.16_LV: Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). is 0.4µm
+logger.info("Executing rule S.DF.16_LV")
+sdf16_l1 = ncomp.outside(nwell).outside(dnwell).inside(sramcore).separation(nwell.outside(dnwell).inside(sramcore), 0.4.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+sdf16_l1.output("S.DF.16_LV", "S.DF.16_LV : Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). : 0.4µm")
+sdf16_l1.forget
+
+# Rule S.CO.3_LV: Poly2 overlap of contact. is 0.04µm
+logger.info("Executing rule S.CO.3_LV")
+sco3_l1 = poly2.inside(sramcore).enclosing(contact.inside(sramcore), 0.04.um, euclidian).polygons(0.001)
+sco3_l2 = contact.inside(sramcore).not_outside(poly2.inside(sramcore)).not(poly2.inside(sramcore))
+sco3_l = sco3_l1.or(sco3_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+sco3_l.output("S.CO.3_LV", "S.CO.3_LV : Poly2 overlap of contact. : 0.04µm")
+sco3_l1.forget
+sco3_l2.forget
+sco3_l.forget
+
+# Rule S.CO.4_LV: COMP overlap of contact. is 0.03µm
+logger.info("Executing rule S.CO.4_LV")
+sco4_l1 = comp.inside(sramcore).enclosing(contact.inside(sramcore), 0.03.um, euclidian).polygons(0.001)
+sco4_l2 = contact.inside(sramcore).not_outside(comp.inside(sramcore)).not(comp.inside(sramcore))
+sco4_l = sco4_l1.or(sco4_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+sco4_l.output("S.CO.4_LV", "S.CO.4_LV : COMP overlap of contact. : 0.03µm")
+sco4_l1.forget
+sco4_l2.forget
+sco4_l.forget
+
+# Rule S.CO.6_ii_LV: (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap
+logger.info("Executing rule S.CO.6_ii_LV")
+sco6_l1 = metal1.and(sramcore).enclosing(contact.inside(sramcore), 0.02.um, euclidian).polygons(0.001).or(contact.inside(sramcore).not(metal1.inside(sramcore))).not_interacting(v5_xtor).not_interacting(dualgate)
+sco6_l1.output("S.CO.6_ii_LV", "S.CO.6_ii_LV : (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap")
+sco6_l1.forget
+
+# Rule S.M1.1_LV: min. metal1 width is 0.22µm
+logger.info("Executing rule S.M1.1_LV")
+sm11_l1 = metal1.and(sramcore).width(0.22.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+sm11_l1.output("S.M1.1_LV", "S.M1.1_LV : min. metal1 width : 0.22µm")
+sm11_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/5v_sram.drc b/rules/klayout/drc/rule_decks/5v_sram.drc
new file mode 100644
index 0000000..22753ae
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/5v_sram.drc
@@ -0,0 +1,280 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (5V SRAM) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+contact = polygons(33 , 0 )
+sramcore = polygons(108, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------5V SRAM---------------------
+#================================================
+
+# Rule S.DF.4c_MV: Min. (Nwell overlap of PCOMP) outside DNWELL. is 0.45µm
+logger.info("Executing rule S.DF.4c_MV")
+sdf4c_l1 = nwell.outside(dnwell).inside(sramcore).enclosing(pcomp.outside(dnwell).inside(sramcore), 0.45.um, euclidian).polygons(0.001)
+sdf4c_l2 = pcomp.outside(dnwell).inside(sramcore).not_outside(nwell.outside(dnwell).inside(sramcore)).not(nwell.outside(dnwell).inside(sramcore))
+sdf4c_l = sdf4c_l1.or(sdf4c_l2).overlapping(v5_xtor).overlapping(dualgate)
+sdf4c_l.output("S.DF.4c_MV", "S.DF.4c_MV : Min. (Nwell overlap of PCOMP) outside DNWELL. : 0.45µm")
+sdf4c_l1.forget
+sdf4c_l2.forget
+sdf4c_l.forget
+
+# Rule S.DF.6_MV: Min. COMP extend beyond gate (it also means source/drain overhang). is 0.32µm
+logger.info("Executing rule S.DF.6_MV")
+sdf6_l1 = comp.inside(sramcore).enclosing(poly2.inside(sramcore), 0.32.um, euclidian).polygons(0.001).overlapping(v5_xtor).overlapping(dualgate)
+sdf6_l1.output("S.DF.6_MV", "S.DF.6_MV : Min. COMP extend beyond gate (it also means source/drain overhang). : 0.32µm")
+sdf6_l1.forget
+
+# Rule S.DF.7_MV: Min. (LVPWELL Spacer to PCOMP) inside DNWELL. is 0.45µm
+logger.info("Executing rule S.DF.7_MV")
+sdf7_l1 = pcomp.inside(dnwell).inside(sramcore).separation(lvpwell.inside(dnwell).inside(sramcore), 0.45.um, euclidian).polygons(0.001).overlapping(v5_xtor).overlapping(dualgate)
+sdf7_l1.output("S.DF.7_MV", "S.DF.7_MV : Min. (LVPWELL Spacer to PCOMP) inside DNWELL. : 0.45µm")
+sdf7_l1.forget
+
+# Rule S.DF.8_MV: Min. (LVPWELL overlap of NCOMP) Inside DNWELL. is 0.45µm
+logger.info("Executing rule S.DF.8_MV")
+sdf8_l1 = lvpwell.inside(dnwell).inside(sramcore).enclosing(ncomp.inside(dnwell).inside(sramcore), 0.45.um, euclidian).polygons(0.001)
+sdf8_l2 = ncomp.inside(dnwell).inside(sramcore).not_outside(lvpwell.inside(dnwell).inside(sramcore)).not(lvpwell.inside(dnwell).inside(sramcore))
+sdf8_l = sdf8_l1.or(sdf8_l2).overlapping(v5_xtor).overlapping(dualgate)
+sdf8_l.output("S.DF.8_MV", "S.DF.8_MV : Min. (LVPWELL overlap of NCOMP) Inside DNWELL. : 0.45µm")
+sdf8_l1.forget
+sdf8_l2.forget
+sdf8_l.forget
+
+# Rule S.DF.16_MV: Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). is 0.45µm
+logger.info("Executing rule S.DF.16_MV")
+sdf16_l1 = ncomp.outside(nwell).outside(dnwell).inside(sramcore).separation(nwell.outside(dnwell).inside(sramcore), 0.45.um, euclidian).polygons(0.001).overlapping(v5_xtor).overlapping(dualgate)
+sdf16_l1.output("S.DF.16_MV", "S.DF.16_MV : Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). : 0.45µm")
+sdf16_l1.forget
+
+# Rule S.PL.5a_MV: Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. is 0.12µm
+logger.info("Executing rule S.PL.5a_MV")
+spl5a_l1 = poly2.inside(sramcore).separation(comp.inside(sramcore), 0.12.um, euclidian).polygons(0.001).overlapping(v5_xtor).overlapping(dualgate)
+spl5a_l1.output("S.PL.5a_MV", "S.PL.5a_MV : Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. : 0.12µm")
+spl5a_l1.forget
+
+# Rule S.PL.5b_MV: Space from field Poly2 to related COMP. is 0.12µm
+logger.info("Executing rule S.PL.5b_MV")
+spl5b_l1 = poly2.inside(sramcore).separation(comp.inside(sramcore), 0.12.um, euclidian).polygons(0.001).overlapping(v5_xtor).overlapping(dualgate)
+spl5b_l1.output("S.PL.5b_MV", "S.PL.5b_MV : Space from field Poly2 to related COMP. : 0.12µm")
+spl5b_l1.forget
+
+# Rule S.CO.4_MV: COMP overlap of contact. is 0.04µm
+logger.info("Executing rule S.CO.4_MV")
+sco4_l1 = comp.inside(sramcore).and(v5_xtor).enclosing(contact.inside(sramcore).and(v5_xtor), 0.04.um, euclidian).polygons(0.001)
+sco4_l2 = contact.inside(sramcore).and(v5_xtor).not_outside(comp.inside(sramcore).and(v5_xtor)).not(comp.inside(sramcore).and(v5_xtor))
+sco4_l = sco4_l1.or(sco4_l2)
+sco4_l.output("S.CO.4_MV", "S.CO.4_MV : COMP overlap of contact. : 0.04µm")
+sco4_l1.forget
+sco4_l2.forget
+sco4_l.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/comp.drc b/rules/klayout/drc/rule_decks/comp.drc
new file mode 100644
index 0000000..f98c607
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/comp.drc
@@ -0,0 +1,634 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (COMP) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+mvsd = polygons(210, 0 )
+mvpsd = polygons(11 , 39)
+schottky_diode = polygons(241, 0 )
+res_mk = polygons(110, 5 )
+cap_mk = polygons(117, 5 )
+mos_cap_mk = polygons(166, 5 )
+drc_bjt = polygons(127, 5 )
+otp_mk = polygons(173, 5 )
+neo_ee_mk = polygons(88 , 17)
+sramcore = polygons(108, 5 )
+ymtp_mk = polygons(86 , 17)
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------COMP----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule FATAL.FATAL: Nplus can’t overlap with pplus
+logger.info("Executing rule FATAL.FATAL")
+fatalfatal_l1 = nplus.and(pplus)
+fatalfatal_l1.output("FATAL.FATAL", "FATAL.FATAL : Nplus can’t overlap with pplus")
+fatalfatal_l1.forget
+
+# Rule DF.1a_3.3V: Min. COMP Width. is 0.22µm
+logger.info("Executing rule DF.1a_3.3V")
+df1a_l1 = comp.width(0.22.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df1a_l1.output("DF.1a_3.3V", "DF.1a_3.3V : Min. COMP Width. : 0.22µm")
+df1a_l1.forget
+
+# Rule DF.1a_5V: Min. COMP Width. is 0.3µm
+logger.info("Executing rule DF.1a_5V")
+df1a_l1 = comp.not_inside(mvsd).not_inside(mvpsd).width(0.3.um, euclidian).polygons(0.001).overlapping(dualgate)
+df1a_l1.output("DF.1a_5V", "DF.1a_5V : Min. COMP Width. : 0.3µm")
+df1a_l1.forget
+
+# rule DF.1b_3.3V is not a DRC check
+
+# rule DF.1b_5V is not a DRC check
+
+# Rule DF.1c_3.3V: Min. COMP Width for MOSCAP. is 1µm
+logger.info("Executing rule DF.1c_3.3V")
+df1c_l1 = comp.and(mos_cap_mk).width(1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df1c_l1.output("DF.1c_3.3V", "DF.1c_3.3V : Min. COMP Width for MOSCAP. : 1µm")
+df1c_l1.forget
+
+# Rule DF.1c_5V: Min. COMP Width for MOSCAP. is 1µm
+logger.info("Executing rule DF.1c_5V")
+df1c_l1 = comp.and(mos_cap_mk).width(1.um, euclidian).polygons(0.001).overlapping(dualgate)
+df1c_l1.output("DF.1c_5V", "DF.1c_5V : Min. COMP Width for MOSCAP. : 1µm")
+df1c_l1.forget
+
+df_2a = comp.not(poly2).edges.and(tgate.edges)
+# Rule DF.2a_3.3V: Min Channel Width. is nil,0.22µm
+logger.info("Executing rule DF.2a_3.3V")
+df2a_l1 = df_2a.with_length(nil,0.22.um).extended(0, 0, 0.001, 0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df2a_l1.output("DF.2a_3.3V", "DF.2a_3.3V : Min Channel Width. : nil,0.22µm")
+df2a_l1.forget
+
+# Rule DF.2a_5V: Min Channel Width. is nil,0.3µm
+logger.info("Executing rule DF.2a_5V")
+df2a_l1 = df_2a.with_length(nil,0.3.um).extended(0, 0, 0.001, 0.001).overlapping(dualgate)
+df2a_l1.output("DF.2a_5V", "DF.2a_5V : Min Channel Width. : nil,0.3µm")
+df2a_l1.forget
+
+df_2a.forget
+df_2b = comp.drc(width <= 100.um).polygons(0.001).not_inside(mos_cap_mk)
+# Rule DF.2b_3.3V: Max. COMP width for all cases except those used for capacitors, marked by ‘MOS_CAP_MK’ layer.
+logger.info("Executing rule DF.2b_3.3V")
+df2b_l1 = comp.not_inside(mos_cap_mk).not_interacting(df_2b).not_interacting(v5_xtor).not_interacting(dualgate)
+df2b_l1.output("DF.2b_3.3V", "DF.2b_3.3V : Max. COMP width for all cases except those used for capacitors, marked by ‘MOS_CAP_MK’ layer.")
+df2b_l1.forget
+
+# Rule DF.2b_5V: Max. COMP width for all cases except those used for capacitors, marked by ‘MOS_CAP_MK’ layer.
+logger.info("Executing rule DF.2b_5V")
+df2b_l1 = comp.not_inside(mos_cap_mk).not_interacting(df_2b).overlapping(dualgate)
+df2b_l1.output("DF.2b_5V", "DF.2b_5V : Max. COMP width for all cases except those used for capacitors, marked by ‘MOS_CAP_MK’ layer.")
+df2b_l1.forget
+
+df_2b.forget
+# Rule DF.3a_3.3V: Min. COMP Space P-substrate tap (PCOMP outside NWELL and DNWELL) can be butted for different voltage devices as the potential is same. is 0.28µm
+logger.info("Executing rule DF.3a_3.3V")
+df3a_l1 = comp.not(otp_mk).space(0.28.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df3a_l1.output("DF.3a_3.3V", "DF.3a_3.3V : Min. COMP Space P-substrate tap (PCOMP outside NWELL and DNWELL) can be butted for different voltage devices as the potential is same. : 0.28µm")
+df3a_l1.forget
+
+# Rule DF.3a_5V: Min. COMP Space P-substrate tap (PCOMP outside NWELL and DNWELL) can be butted for different voltage devices as the potential is same. is 0.36µm
+logger.info("Executing rule DF.3a_5V")
+df3a_l1 = comp.not(otp_mk).space(0.36.um, euclidian).polygons(0.001).overlapping(dualgate)
+df3a_l1.output("DF.3a_5V", "DF.3a_5V : Min. COMP Space P-substrate tap (PCOMP outside NWELL and DNWELL) can be butted for different voltage devices as the potential is same. : 0.36µm")
+df3a_l1.forget
+
+df_3b_same_well = ncomp.inside(nwell).not_outside(pcomp.inside(nwell)).or(ncomp.inside(lvpwell).not_outside(pcomp.inside(lvpwell)))
+df_3b_moscap = ncomp.inside(nwell).interacting(pcomp.inside(nwell)).or(ncomp.inside(lvpwell).interacting(pcomp.inside(lvpwell))).inside(mos_cap_mk)
+# Rule DF.3b_3.3V: Min./Max. NCOMP Space to PCOMP in the same well for butted COMP (MOSCAP butting is not allowed).
+logger.info("Executing rule DF.3b_3.3V")
+df3b_l1 = df_3b_same_well.or(df_3b_moscap).not_interacting(v5_xtor).not_interacting(dualgate)
+df3b_l1.output("DF.3b_3.3V", "DF.3b_3.3V : Min./Max. NCOMP Space to PCOMP in the same well for butted COMP (MOSCAP butting is not allowed).")
+df3b_l1.forget
+
+# Rule DF.3b_5V: Min./Max. NCOMP Space to PCOMP in the same well for butted COMP(MOSCAP butting is not allowed).
+logger.info("Executing rule DF.3b_5V")
+df3b_l1 = df_3b_same_well.or(df_3b_moscap).overlapping(dualgate)
+df3b_l1.output("DF.3b_5V", "DF.3b_5V : Min./Max. NCOMP Space to PCOMP in the same well for butted COMP(MOSCAP butting is not allowed).")
+df3b_l1.forget
+
+df_3b_same_well.forget
+df_3b_moscap.forget
+# Rule DF.3c_3.3V: Min. COMP Space in BJT area (area marked by DRC_BJT layer). is 0.32µm
+logger.info("Executing rule DF.3c_3.3V")
+df3c_l1 = comp.inside(drc_bjt).space(0.32.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df3c_l1.output("DF.3c_3.3V", "DF.3c_3.3V : Min. COMP Space in BJT area (area marked by DRC_BJT layer). : 0.32µm")
+df3c_l1.forget
+
+# Rule DF.3c_5V: Min. COMP Space in BJT area (area marked by DRC_BJT layer) hasn’t been assessed.
+logger.info("Executing rule DF.3c_5V")
+df3c_l1 = comp.interacting(comp.inside(drc_bjt).and(dualgate).space(10.um, euclidian).polygons(0.001))
+df3c_l1.output("DF.3c_5V", "DF.3c_5V : Min. COMP Space in BJT area (area marked by DRC_BJT layer) hasn’t been assessed.")
+df3c_l1.forget
+
+ntap_dnwell = ncomp.not_interacting(tgate).inside(dnwell)
+# Rule DF.4a_3.3V: Min. (LVPWELL Space to NCOMP well tap) inside DNWELL. is 0.12µm
+logger.info("Executing rule DF.4a_3.3V")
+df4a_l1 = ntap_dnwell.separation(lvpwell.inside(dnwell), 0.12.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df4a_l1.output("DF.4a_3.3V", "DF.4a_3.3V : Min. (LVPWELL Space to NCOMP well tap) inside DNWELL. : 0.12µm")
+df4a_l1.forget
+
+# Rule DF.4a_5V: Min. (LVPWELL Space to NCOMP well tap) inside DNWELL. is 0.16µm
+logger.info("Executing rule DF.4a_5V")
+df4a_l1 = ntap_dnwell.separation(lvpwell.inside(dnwell), 0.16.um, euclidian).polygons(0.001).overlapping(dualgate)
+df4a_l1.output("DF.4a_5V", "DF.4a_5V : Min. (LVPWELL Space to NCOMP well tap) inside DNWELL. : 0.16µm")
+df4a_l1.forget
+
+# Rule DF.4b_3.3V: Min. DNWELL overlap of NCOMP well tap. is 0.62µm
+logger.info("Executing rule DF.4b_3.3V")
+df4b_l1 = dnwell.enclosing(ncomp.not_interacting(tgate), 0.62.um, euclidian).polygons(0.001)
+df4b_l2 = ncomp.not_interacting(tgate).not_outside(dnwell).not(dnwell)
+df4b_l = df4b_l1.or(df4b_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df4b_l.output("DF.4b_3.3V", "DF.4b_3.3V : Min. DNWELL overlap of NCOMP well tap. : 0.62µm")
+df4b_l1.forget
+df4b_l2.forget
+df4b_l.forget
+
+# Rule DF.4b_5V: Min. DNWELL overlap of NCOMP well tap. is 0.66µm
+logger.info("Executing rule DF.4b_5V")
+df4b_l1 = dnwell.enclosing(ncomp.not_interacting(tgate), 0.66.um, euclidian).polygons(0.001)
+df4b_l2 = ncomp.not_interacting(tgate).not_outside(dnwell).not(dnwell)
+df4b_l = df4b_l1.or(df4b_l2).overlapping(dualgate)
+df4b_l.output("DF.4b_5V", "DF.4b_5V : Min. DNWELL overlap of NCOMP well tap. : 0.66µm")
+df4b_l1.forget
+df4b_l2.forget
+df4b_l.forget
+
+ntap_dnwell.forget
+nwell_n_dnwell = nwell.outside(dnwell)
+# Rule DF.4c_3.3V: Min. (Nwell overlap of PCOMP) outside DNWELL. is 0.43µm
+logger.info("Executing rule DF.4c_3.3V")
+df4c_l1 = nwell_n_dnwell.outside(sramcore).enclosing(pcomp.outside(dnwell), 0.43.um, euclidian).polygons(0.001)
+df4c_l2 = pcomp.outside(dnwell).not_outside(nwell_n_dnwell.outside(sramcore)).not(nwell_n_dnwell.outside(sramcore))
+df4c_l = df4c_l1.or(df4c_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df4c_l.output("DF.4c_3.3V", "DF.4c_3.3V : Min. (Nwell overlap of PCOMP) outside DNWELL. : 0.43µm")
+df4c_l1.forget
+df4c_l2.forget
+df4c_l.forget
+
+# Rule DF.4c_5V: Min. (Nwell overlap of PCOMP) outside DNWELL. is 0.6µm
+logger.info("Executing rule DF.4c_5V")
+df4c_l1 = nwell_n_dnwell.outside(sramcore).enclosing(pcomp.outside(dnwell), 0.6.um, euclidian).polygons(0.001)
+df4c_l2 = pcomp.outside(dnwell).not_outside(nwell_n_dnwell.outside(sramcore)).not(nwell_n_dnwell.outside(sramcore))
+df4c_l = df4c_l1.or(df4c_l2).overlapping(dualgate)
+df4c_l.output("DF.4c_5V", "DF.4c_5V : Min. (Nwell overlap of PCOMP) outside DNWELL. : 0.6µm")
+df4c_l1.forget
+df4c_l2.forget
+df4c_l.forget
+
+# Rule DF.4d_3.3V: Min. (Nwell overlap of NCOMP) outside DNWELL. is 0.12µm
+logger.info("Executing rule DF.4d_3.3V")
+df4d_l1 = nwell_n_dnwell.not_inside(ymtp_mk).not_inside(neo_ee_mk).enclosing(ncomp.outside(dnwell).not_inside(ymtp_mk), 0.12.um, euclidian).polygons(0.001)
+df4d_l2 = ncomp.outside(dnwell).not_inside(ymtp_mk).not_outside(nwell_n_dnwell.not_inside(ymtp_mk).not_inside(neo_ee_mk)).not(nwell_n_dnwell.not_inside(ymtp_mk).not_inside(neo_ee_mk))
+df4d_l = df4d_l1.or(df4d_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df4d_l.output("DF.4d_3.3V", "DF.4d_3.3V : Min. (Nwell overlap of NCOMP) outside DNWELL. : 0.12µm")
+df4d_l1.forget
+df4d_l2.forget
+df4d_l.forget
+
+# Rule DF.4d_5V: Min. (Nwell overlap of NCOMP) outside DNWELL. is 0.16µm
+logger.info("Executing rule DF.4d_5V")
+df4d_l1 = nwell_n_dnwell.not_inside(ymtp_mk).enclosing(ncomp.outside(dnwell).not_inside(ymtp_mk), 0.16.um, euclidian).polygons(0.001)
+df4d_l2 = ncomp.outside(dnwell).not_inside(ymtp_mk).not_outside(nwell_n_dnwell.not_inside(ymtp_mk)).not(nwell_n_dnwell.not_inside(ymtp_mk))
+df4d_l = df4d_l1.or(df4d_l2).overlapping(dualgate)
+df4d_l.output("DF.4d_5V", "DF.4d_5V : Min. (Nwell overlap of NCOMP) outside DNWELL. : 0.16µm")
+df4d_l1.forget
+df4d_l2.forget
+df4d_l.forget
+
+nwell_n_dnwell.forget
+# Rule DF.4e_3.3V: Min. DNWELL overlap of PCOMP. is 0.93µm
+logger.info("Executing rule DF.4e_3.3V")
+df4e_l1 = dnwell.enclosing(pcomp, 0.93.um, euclidian).polygons(0.001)
+df4e_l2 = pcomp.not_outside(dnwell).not(dnwell)
+df4e_l = df4e_l1.or(df4e_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df4e_l.output("DF.4e_3.3V", "DF.4e_3.3V : Min. DNWELL overlap of PCOMP. : 0.93µm")
+df4e_l1.forget
+df4e_l2.forget
+df4e_l.forget
+
+# Rule DF.4e_5V: Min. DNWELL overlap of PCOMP. is 1.1µm
+logger.info("Executing rule DF.4e_5V")
+df4e_l1 = dnwell.enclosing(pcomp, 1.1.um, euclidian).polygons(0.001)
+df4e_l2 = pcomp.not_outside(dnwell).not(dnwell)
+df4e_l = df4e_l1.or(df4e_l2).overlapping(dualgate)
+df4e_l.output("DF.4e_5V", "DF.4e_5V : Min. DNWELL overlap of PCOMP. : 1.1µm")
+df4e_l1.forget
+df4e_l2.forget
+df4e_l.forget
+
+pwell_dnwell = lvpwell.inside(dnwell)
+# Rule DF.5_3.3V: Min. (LVPWELL overlap of PCOMP well tap) inside DNWELL. is 0.12µm
+logger.info("Executing rule DF.5_3.3V")
+df5_l1 = pwell_dnwell.enclosing(pcomp.outside(nwell), 0.12.um, euclidian).polygons(0.001)
+df5_l2 = pcomp.outside(nwell).not_outside(pwell_dnwell).not(pwell_dnwell)
+df5_l = df5_l1.or(df5_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df5_l.output("DF.5_3.3V", "DF.5_3.3V : Min. (LVPWELL overlap of PCOMP well tap) inside DNWELL. : 0.12µm")
+df5_l1.forget
+df5_l2.forget
+df5_l.forget
+
+# Rule DF.5_5V: Min. (LVPWELL overlap of PCOMP well tap) inside DNWELL. is 0.16µm
+logger.info("Executing rule DF.5_5V")
+df5_l1 = pwell_dnwell.enclosing(pcomp.outside(nwell), 0.16.um, euclidian).polygons(0.001)
+df5_l2 = pcomp.outside(nwell).not_outside(pwell_dnwell).not(pwell_dnwell)
+df5_l = df5_l1.or(df5_l2).overlapping(dualgate)
+df5_l.output("DF.5_5V", "DF.5_5V : Min. (LVPWELL overlap of PCOMP well tap) inside DNWELL. : 0.16µm")
+df5_l1.forget
+df5_l2.forget
+df5_l.forget
+
+# Rule DF.6_3.3V: Min. COMP extend beyond gate (it also means source/drain overhang). is 0.24µm
+logger.info("Executing rule DF.6_3.3V")
+df6_l1 = comp.not(otp_mk).not_inside(ymtp_mk).enclosing(poly2.not_inside(ymtp_mk), 0.24.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df6_l1.output("DF.6_3.3V", "DF.6_3.3V : Min. COMP extend beyond gate (it also means source/drain overhang). : 0.24µm")
+df6_l1.forget
+
+# Rule DF.6_5V: Min. COMP extend beyond gate (it also means source/drain overhang). is 0.4µm
+logger.info("Executing rule DF.6_5V")
+df6_l1 = comp.not(otp_mk).not_inside(mvpsd).not_inside(mvsd).not_inside(ymtp_mk).outside(sramcore).enclosing(poly2.not_inside(ymtp_mk), 0.4.um, euclidian).polygons(0.001).overlapping(dualgate)
+df6_l1.output("DF.6_5V", "DF.6_5V : Min. COMP extend beyond gate (it also means source/drain overhang). : 0.4µm")
+df6_l1.forget
+
+# Rule DF.7_3.3V: Min. (LVPWELL Spacer to PCOMP) inside DNWELL. is 0.43µm
+logger.info("Executing rule DF.7_3.3V")
+df7_l1 = pcomp.inside(dnwell).separation(pwell_dnwell, 0.43.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df7_l1.output("DF.7_3.3V", "DF.7_3.3V : Min. (LVPWELL Spacer to PCOMP) inside DNWELL. : 0.43µm")
+df7_l1.forget
+
+# Rule DF.7_5V: Min. (LVPWELL Spacer to PCOMP) inside DNWELL. is 0.6µm
+logger.info("Executing rule DF.7_5V")
+df7_l1 = pcomp.inside(dnwell).outside(sramcore).separation(pwell_dnwell, 0.6.um, euclidian).polygons(0.001).overlapping(dualgate)
+df7_l1.output("DF.7_5V", "DF.7_5V : Min. (LVPWELL Spacer to PCOMP) inside DNWELL. : 0.6µm")
+df7_l1.forget
+
+# Rule DF.8_3.3V: Min. (LVPWELL overlap of NCOMP) Inside DNWELL. is 0.43µm
+logger.info("Executing rule DF.8_3.3V")
+df8_l1 = pwell_dnwell.enclosing(ncomp.inside(dnwell), 0.43.um, euclidian).polygons(0.001)
+df8_l2 = ncomp.inside(dnwell).not_outside(pwell_dnwell).not(pwell_dnwell)
+df8_l = df8_l1.or(df8_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+df8_l.output("DF.8_3.3V", "DF.8_3.3V : Min. (LVPWELL overlap of NCOMP) Inside DNWELL. : 0.43µm")
+df8_l1.forget
+df8_l2.forget
+df8_l.forget
+
+# Rule DF.8_5V: Min. (LVPWELL overlap of NCOMP) Inside DNWELL. is 0.6µm
+logger.info("Executing rule DF.8_5V")
+df8_l1 = pwell_dnwell.outside(sramcore).enclosing(ncomp.inside(dnwell), 0.6.um, euclidian).polygons(0.001)
+df8_l2 = ncomp.inside(dnwell).not_outside(pwell_dnwell.outside(sramcore)).not(pwell_dnwell.outside(sramcore))
+df8_l = df8_l1.or(df8_l2).overlapping(dualgate)
+df8_l.output("DF.8_5V", "DF.8_5V : Min. (LVPWELL overlap of NCOMP) Inside DNWELL. : 0.6µm")
+df8_l1.forget
+df8_l2.forget
+df8_l.forget
+
+pwell_dnwell.forget
+# Rule DF.9_3.3V: Min. COMP area (um2). is 0.2025µm²
+logger.info("Executing rule DF.9_3.3V")
+df9_l1 = comp.not(otp_mk).with_area(nil, 0.2025.um).not_interacting(v5_xtor).not_interacting(dualgate)
+df9_l1.output("DF.9_3.3V", "DF.9_3.3V : Min. COMP area (um2). : 0.2025µm²")
+df9_l1.forget
+# Rule DF.9_5V: Min. COMP area (um2). is 0.2025µm²
+logger.info("Executing rule DF.9_5V")
+df9_l1 = comp.not(otp_mk).with_area(nil, 0.2025.um).overlapping(dualgate)
+df9_l1.output("DF.9_5V", "DF.9_5V : Min. COMP area (um2). : 0.2025µm²")
+df9_l1.forget
+# Rule DF.10_3.3V: Min. field area (um2). is 0.26µm²
+logger.info("Executing rule DF.10_3.3V")
+df10_l1 = comp.holes.not(comp).with_area(nil, 0.26.um).not_interacting(v5_xtor).not_interacting(dualgate)
+df10_l1.output("DF.10_3.3V", "DF.10_3.3V : Min. field area (um2). : 0.26µm²")
+df10_l1.forget
+# Rule DF.10_5V: Min. field area (um2). is 0.26µm²
+logger.info("Executing rule DF.10_5V")
+df10_l1 = comp.holes.not(comp).with_area(nil, 0.26.um).overlapping(dualgate)
+df10_l1.output("DF.10_5V", "DF.10_5V : Min. field area (um2). : 0.26µm²")
+df10_l1.forget
+comp_butt = comp.interacting(ncomp.interacting(pcomp).outside(pcomp))
+# Rule DF.11_3.3V: Min. Length of butting COMP edge. is 0.3µm
+logger.info("Executing rule DF.11_3.3V")
+df11_l1 = comp_butt.width(0.3.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df11_l1.output("DF.11_3.3V", "DF.11_3.3V : Min. Length of butting COMP edge. : 0.3µm")
+df11_l1.forget
+
+# Rule DF.11_5V: Min. Length of butting COMP edge. is 0.3µm
+logger.info("Executing rule DF.11_5V")
+df11_l1 = comp_butt.width(0.3.um, euclidian).polygons(0.001).overlapping(dualgate)
+df11_l1.output("DF.11_5V", "DF.11_5V : Min. Length of butting COMP edge. : 0.3µm")
+df11_l1.forget
+
+comp_butt.forget
+# Rule DF.12_3.3V: COMP not covered by Nplus or Pplus is forbidden (except those COMP under marking).
+logger.info("Executing rule DF.12_3.3V")
+df12_l1 = comp.not_interacting(schottky_diode).not(nplus.or(pplus)).not_interacting(v5_xtor).not_interacting(dualgate)
+df12_l1.output("DF.12_3.3V", "DF.12_3.3V : COMP not covered by Nplus or Pplus is forbidden (except those COMP under marking).")
+df12_l1.forget
+
+# Rule DF.12_5V: COMP not covered by Nplus or Pplus is forbidden (except those COMP under marking).
+logger.info("Executing rule DF.12_5V")
+df12_l1 = comp.not_interacting(schottky_diode).not(nplus.or(pplus)).overlapping(dualgate)
+df12_l1.output("DF.12_5V", "DF.12_5V : COMP not covered by Nplus or Pplus is forbidden (except those COMP under marking).")
+df12_l1.forget
+
+df13_ncomp = ncomp.inside(nwell.covering(ncomp).covering(pcomp))
+df13_pcomp = pcomp.inside(nwell.covering(ncomp).covering(pcomp))
+# Rule DF.13_3.3V: Max distance of Nwell tap (NCOMP inside Nwell) from (PCOMP inside Nwell).
+logger.info("Executing rule DF.13_3.3V")
+df13_l1 = df13_ncomp.not_interacting(df13_pcomp.sized(20.um)).not_interacting(v5_xtor).not_interacting(dualgate)
+df13_l1.output("DF.13_3.3V", "DF.13_3.3V : Max distance of Nwell tap (NCOMP inside Nwell) from (PCOMP inside Nwell).")
+df13_l1.forget
+
+# Rule DF.13_5V: Max distance of Nwell tap (NCOMP inside Nwell) from (PCOMP inside Nwell).
+logger.info("Executing rule DF.13_5V")
+df13_l1 = df13_ncomp.not_interacting(df13_pcomp.sized(15.um)).overlapping(dualgate)
+df13_l1.output("DF.13_5V", "DF.13_5V : Max distance of Nwell tap (NCOMP inside Nwell) from (PCOMP inside Nwell).")
+df13_l1.forget
+
+df13_ncomp.forget
+df13_pcomp.forget
+# Rule DF.14_3.3V: Max distance of substrate tap (PCOMP outside Nwell) from (NCOMP outside Nwell).
+logger.info("Executing rule DF.14_3.3V")
+df14_l1 = pcomp.outside(nwell).not_interacting(ncomp.outside(nwell).sized(20.um)).not_interacting(v5_xtor).not_interacting(dualgate)
+df14_l1.output("DF.14_3.3V", "DF.14_3.3V : Max distance of substrate tap (PCOMP outside Nwell) from (NCOMP outside Nwell).")
+df14_l1.forget
+
+# Rule DF.14_5V: Max distance of substrate tap (PCOMP outside Nwell) from (NCOMP outside Nwell).
+logger.info("Executing rule DF.14_5V")
+df14_l1 = pcomp.outside(nwell).not_interacting(ncomp.outside(nwell).sized(15.um)).overlapping(dualgate)
+df14_l1.output("DF.14_5V", "DF.14_5V : Max distance of substrate tap (PCOMP outside Nwell) from (NCOMP outside Nwell).")
+df14_l1.forget
+
+# rule DF.15a_3.3V is not a DRC check
+
+# rule DF.15a_5V is not a DRC check
+
+# rule DF.15b_3.3V is not a DRC check
+
+# rule DF.15b_5V is not a DRC check
+
+ncomp_df16 = ncomp.outside(nwell).outside(dnwell)
+# Rule DF.16_3.3V: Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). is 0.43µm
+logger.info("Executing rule DF.16_3.3V")
+df16_l1 = ncomp_df16.not_inside(ymtp_mk).outside(sramcore).separation(nwell.outside(dnwell).not_inside(ymtp_mk), 0.43.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df16_l1.output("DF.16_3.3V", "DF.16_3.3V : Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). : 0.43µm")
+df16_l1.forget
+
+# Rule DF.16_5V: Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). is 0.6µm
+logger.info("Executing rule DF.16_5V")
+df16_l1 = ncomp_df16.not_inside(ymtp_mk).outside(sramcore).separation(nwell.outside(dnwell).not_inside(ymtp_mk), 0.6.um, euclidian).polygons(0.001).overlapping(dualgate)
+df16_l1.output("DF.16_5V", "DF.16_5V : Min. space from (Nwell outside DNWELL) to (NCOMP outside Nwell and DNWELL). : 0.6µm")
+df16_l1.forget
+
+pcomp_df17 = pcomp.outside(nwell).outside(dnwell)
+# Rule DF.17_3.3V: Min. space from (Nwell Outside DNWELL) to (PCOMP outside Nwell and DNWELL). is 0.12µm
+logger.info("Executing rule DF.17_3.3V")
+df17_l1 = pcomp_df17.separation(nwell.outside(dnwell), 0.12.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df17_l1.output("DF.17_3.3V", "DF.17_3.3V : Min. space from (Nwell Outside DNWELL) to (PCOMP outside Nwell and DNWELL). : 0.12µm")
+df17_l1.forget
+
+# Rule DF.17_5V: Min. space from (Nwell Outside DNWELL) to (PCOMP outside Nwell and DNWELL). is 0.16µm
+logger.info("Executing rule DF.17_5V")
+df17_l1 = pcomp_df17.separation(nwell.outside(dnwell), 0.16.um, euclidian).polygons(0.001).overlapping(dualgate)
+df17_l1.output("DF.17_5V", "DF.17_5V : Min. space from (Nwell Outside DNWELL) to (PCOMP outside Nwell and DNWELL). : 0.16µm")
+df17_l1.forget
+
+# Rule DF.18_3.3V: Min. DNWELL space to (PCOMP outside Nwell and DNWELL). is 2.5µm
+logger.info("Executing rule DF.18_3.3V")
+df18_l1 = pcomp_df17.separation(dnwell, 2.5.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df18_l1.output("DF.18_3.3V", "DF.18_3.3V : Min. DNWELL space to (PCOMP outside Nwell and DNWELL). : 2.5µm")
+df18_l1.forget
+
+# Rule DF.18_5V: Min. DNWELL space to (PCOMP outside Nwell and DNWELL). is 2.5µm
+logger.info("Executing rule DF.18_5V")
+df18_l1 = pcomp_df17.separation(dnwell, 2.5.um, euclidian).polygons(0.001).overlapping(dualgate)
+df18_l1.output("DF.18_5V", "DF.18_5V : Min. DNWELL space to (PCOMP outside Nwell and DNWELL). : 2.5µm")
+df18_l1.forget
+
+pcomp_df17.forget
+# Rule DF.19_3.3V: Min. DNWELL space to (NCOMP outside Nwell and DNWELL). is 3.2µm
+logger.info("Executing rule DF.19_3.3V")
+df19_l1 = ncomp_df16.separation(dnwell, 3.2.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+df19_l1.output("DF.19_3.3V", "DF.19_3.3V : Min. DNWELL space to (NCOMP outside Nwell and DNWELL). : 3.2µm")
+df19_l1.forget
+
+# Rule DF.19_5V: Min. DNWELL space to (NCOMP outside Nwell and DNWELL). is 3.28µm
+logger.info("Executing rule DF.19_5V")
+df19_l1 = ncomp_df16.separation(dnwell, 3.28.um, euclidian).polygons(0.001).overlapping(dualgate)
+df19_l1.output("DF.19_5V", "DF.19_5V : Min. DNWELL space to (NCOMP outside Nwell and DNWELL). : 3.28µm")
+df19_l1.forget
+
+ncomp_df16.forget
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/contact.drc b/rules/klayout/drc/rule_decks/contact.drc
new file mode 100644
index 0000000..c11b86c
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/contact.drc
@@ -0,0 +1,361 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (CONTACT) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+contact = polygons(33 , 0 )
+metal1 = polygons(34 , 0 )
+mvsd = polygons(210, 0 )
+mvpsd = polygons(11 , 39)
+otp_mk = polygons(173, 5 )
+sramcore = polygons(108, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------CONTACT---------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule CO.1: Min/max contact size. is 0.22µm
+logger.info("Executing rule CO.1")
+co1_l1 = contact.edges.without_length(0.22.um).extended(0, 0, 0.001, 0.001)
+co1_l1.output("CO.1", "CO.1 : Min/max contact size. : 0.22µm")
+co1_l1.forget
+
+# Rule CO.2a: min. contact spacing is 0.25µm
+logger.info("Executing rule CO.2a")
+co2a_l1 = contact.space(0.25.um, euclidian).polygons(0.001)
+co2a_l1.output("CO.2a", "CO.2a : min. contact spacing : 0.25µm")
+co2a_l1.forget
+
+merged_co1 = contact.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.63.um , nil).extents.inside(metal1)
+contact_mask = merged_co1.size(1).not(contact).with_holes(16, nil)
+selected_co1 = contact.interacting(contact_mask)
+# Rule CO.2b: Space in 4x4 or larger contact array. is 0.28µm
+logger.info("Executing rule CO.2b")
+co2b_l1 = selected_co1.space(0.28.um, euclidian).polygons(0.001)
+co2b_l1.output("CO.2b", "CO.2b : Space in 4x4 or larger contact array. : 0.28µm")
+co2b_l1.forget
+
+merged_co1.forget
+contact_mask.forget
+selected_co1.forget
+# Rule CO.3: Poly2 overlap of contact. is 0.07µm
+logger.info("Executing rule CO.3")
+co3_l1 = poly2.enclosing(contact.outside(sramcore), 0.07.um, euclidian).polygons(0.001)
+co3_l2 = contact.outside(sramcore).not_outside(poly2).not(poly2)
+co3_l = co3_l1.or(co3_l2)
+co3_l.output("CO.3", "CO.3 : Poly2 overlap of contact. : 0.07µm")
+co3_l1.forget
+co3_l2.forget
+co3_l.forget
+
+# Rule CO.4: COMP overlap of contact. is 0.07µm
+logger.info("Executing rule CO.4")
+co4_l1 = comp.not(mvsd).not(mvpsd).enclosing(contact.outside(sramcore), 0.07.um, euclidian).polygons(0.001)
+co4_l2 = contact.outside(sramcore).not_outside(comp.not(mvsd).not(mvpsd)).not(comp.not(mvsd).not(mvpsd))
+co4_l = co4_l1.or(co4_l2)
+co4_l.output("CO.4", "CO.4 : COMP overlap of contact. : 0.07µm")
+co4_l1.forget
+co4_l2.forget
+co4_l.forget
+
+co_5a_ncomp_butted = ncomp.not(pplus).interacting(pcomp.not(nplus)).not_overlapping(pcomp.not(nplus))
+# Rule CO.5a: Nplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). is 0.1µm
+logger.info("Executing rule CO.5a")
+co5a_l1 = co_5a_ncomp_butted.enclosing(contact, 0.1.um, euclidian).polygons(0.001)
+co5a_l2 = contact.not_outside(co_5a_ncomp_butted).not(co_5a_ncomp_butted)
+co5a_l = co5a_l1.or(co5a_l2)
+co5a_l.output("CO.5a", "CO.5a : Nplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). : 0.1µm")
+co5a_l1.forget
+co5a_l2.forget
+co5a_l.forget
+
+co_5a_ncomp_butted.forget
+co_5b_pcomp_butted = pcomp.not(nplus).interacting(ncomp.not(pplus)).not_overlapping(ncomp.not(pplus))
+# Rule CO.5b: Pplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). is 0.1µm
+logger.info("Executing rule CO.5b")
+co5b_l1 = co_5b_pcomp_butted.enclosing(contact, 0.1.um, euclidian).polygons(0.001)
+co5b_l2 = contact.not_outside(co_5b_pcomp_butted).not(co_5b_pcomp_butted)
+co5b_l = co5b_l1.or(co5b_l2)
+co5b_l.output("CO.5b", "CO.5b : Pplus overlap of contact on COMP (Only for contacts to butted Nplus and Pplus COMP areas). : 0.1µm")
+co5b_l1.forget
+co5b_l2.forget
+co5b_l.forget
+
+co_5b_pcomp_butted.forget
+# Rule CO.6: Metal1 overlap of contact.
+logger.info("Executing rule CO.6")
+co6_l1 = metal1.enclosing(contact, 0.005.um, euclidian).polygons(0.001).or(contact.not(metal1))
+co6_l1.output("CO.6", "CO.6 : Metal1 overlap of contact.")
+co6_l1.forget
+
+cop6a_cond = metal1.drc( width <= 0.34.um).with_length(0.24.um,nil,both)
+cop6a_eol = metal1.edges.with_length(nil, 0.34.um).interacting(cop6a_cond.first_edges).interacting(cop6a_cond.second_edges).not(cop6a_cond.first_edges).not(cop6a_cond.second_edges)
+# Rule CO.6a: (i) Metal1 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule CO.6a")
+co6a_l1 = cop6a_eol.enclosing(contact.edges,0.06.um, projection).polygons(0.001)
+co6a_l1.output("CO.6a", "CO.6a : (i) Metal1 (< 0.34um) end-of-line overlap. : 0.06µm")
+co6a_l1.forget
+
+cop6a_cond.forget
+cop6a_eol.forget
+co_6b_1 = contact.edges.interacting(contact.drc(enclosed(metal1, projection) < 0.04.um).edges.centers(0, 0.5))
+co_6b_2 = contact.edges.interacting(contact.drc(0.04.um <= enclosed(metal1, projection) < 0.06.um).centers(0, 0.5))
+co_6b_3 = co_6b_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule CO.6b: (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap is 0.06µm
+logger.info("Executing rule CO.6b")
+co6b_l1 = co_6b_2.not_in(co_6b_1).interacting(co_6b_1).or(co_6b_1.interacting(co_6b_3)).not(sramcore).enclosed(metal1.outside(sramcore).edges, 0.06.um).polygons(0.001)
+co6b_l1.output("CO.6b", "CO.6b : (ii) If Metal1 overlaps contact by < 0.04um on one side, adjacent metal1 edges overlap : 0.06µm")
+co6b_l1.forget
+
+co_6b_1.forget
+co_6b_2.forget
+co_6b_3.forget
+# rule CO.6c is not a DRC check
+
+# Rule CO.7: Space from COMP contact to Poly2 on COMP. is 0.15µm
+logger.info("Executing rule CO.7")
+co7_l1 = contact.not_outside(comp).not(otp_mk).separation(tgate.not(otp_mk), 0.15.um, euclidian).polygons(0.001)
+co7_l1.output("CO.7", "CO.7 : Space from COMP contact to Poly2 on COMP. : 0.15µm")
+co7_l1.forget
+
+# Rule CO.8: Space from Poly2 contact to COMP. is 0.17µm
+logger.info("Executing rule CO.8")
+co8_l1 = contact.not_outside(poly2).separation(comp, 0.17.um, euclidian).polygons(0.001)
+co8_l1.output("CO.8", "CO.8 : Space from Poly2 contact to COMP. : 0.17µm")
+co8_l1.forget
+
+# Rule CO.9: Contact on NCOMP to PCOMP butting edge is forbidden (contact must not straddle butting edge).
+logger.info("Executing rule CO.9")
+co9_l1 = contact.interacting(ncomp.edges.and(pcomp.edges))
+co9_l1.output("CO.9", "CO.9 : Contact on NCOMP to PCOMP butting edge is forbidden (contact must not straddle butting edge).")
+co9_l1.forget
+
+# Rule CO.10: Contact on Poly2 gate over COMP is forbidden.
+logger.info("Executing rule CO.10")
+co10_l1 = contact.not_outside(tgate)
+co10_l1.output("CO.10", "CO.10 : Contact on Poly2 gate over COMP is forbidden.")
+co10_l1.forget
+
+# Rule CO.11: Contact on field oxide is forbidden.
+logger.info("Executing rule CO.11")
+co11_l1 = contact.not(comp.or(poly2))
+co11_l1.output("CO.11", "CO.11 : Contact on field oxide is forbidden.")
+co11_l1.forget
+
+end #FEOL
+
+if BEOL
+logger.info("BEOL section")
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/dnwell.drc b/rules/klayout/drc/rule_decks/dnwell.drc
new file mode 100644
index 0000000..c05adeb
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/dnwell.drc
@@ -0,0 +1,361 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (DNWELL) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+sab = polygons(49 , 0 )
+nat = polygons(5 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+#================================================
+#------------- LAYERS CONNECTIONS ---------------
+#================================================
+
+if CONNECTIVITY_RULES
+
+ logger.info("Construct connectivity for the design.")
+
+ connect(dnwell, ncomp)
+ connect(ncomp, contact)
+ connect(pcomp, contact)
+ connect(lvpwell, ncomp)
+ connect(nwell, ncomp)
+ connect(natcompsd, contact)
+ connect(mvsd, ncomp)
+ connect(mvpsd, pcomp)
+ connect(contact, metal1)
+ connect(metal1, via1)
+ connect(via1, metal2)
+ connect(metal2, via2)
+ connect(via2, metal3)
+ connect(metal3, via3)
+ connect(via3, metal4)
+ connect(metal4, via4)
+ connect(via4, metal5)
+ connect(metal5, via5)
+ connect(via5, metaltop)
+
+end #CONNECTIVITY_RULES
+
+#================================================
+#------------ PRE-DEFINED FUNCTIONS -------------
+#================================================
+
+def conn_space(layer,conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer.space(conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
+ singularity_errors = layer.space(0.001.um)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
+ return connected_output, unconnected_output
+end
+
+def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons(0.001)
+ return connected_output, unconnected_output
+end
+
+# === IMPLICIT EXTRACTION ===
+if CONNECTIVITY_RULES
+ logger.info("Connectivity rules enabled, Netlist object will be generated.")
+ netlist
+end #CONNECTIVITY_RULES
+
+# === LAYOUT EXTENT ===
+CHIP = extent.sized(0.0)
+
+logger.info("Total area of the design is #{CHIP.area()} um^2.")
+
+
+
+#================================================
+#---------------------DNWELL---------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule DN.1: Min. DNWELL Width is 1.7µm
+logger.info("Executing rule DN.1")
+dn1_l1 = dnwell.width(1.7.um, euclidian).polygons(0.001)
+dn1_l1.output("DN.1", "DN.1 : Min. DNWELL Width : 1.7µm")
+dn1_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_dnwell, unconnected_dnwell = conn_space(dnwell, 2.5, 5.42, euclidian)
+
+# Rule DN.2a: Min. DNWELL Space (Equi-potential), Merge if the space is less than is 2.5µm
+logger.info("Executing rule DN.2a")
+dn2a_l1 = connected_dnwell
+dn2a_l1.output("DN.2a", "DN.2a : Min. DNWELL Space (Equi-potential), Merge if the space is less than : 2.5µm")
+dn2a_l1.forget
+
+# Rule DN.2b: Min. DNWELL Space (Different potential) is 5.42µm
+logger.info("Executing rule DN.2b")
+dn2b_l1 = unconnected_dnwell
+dn2b_l1.output("DN.2b", "DN.2b : Min. DNWELL Space (Different potential) : 5.42µm")
+dn2b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule DN.2b_: Min. DNWELL Space (Different potential) is 5.42µm
+logger.info("Executing rule DN.2b_")
+dn2b_l1 = dnwell.isolated(5.42.um, euclidian).polygons(0.001)
+dn2b_l1.output("DN.2b_", "DN.2b_ : Min. DNWELL Space (Different potential) : 5.42µm")
+dn2b_l1.forget
+
+end #CONNECTIVITY_RULES
+
+dn3_1 = dnwell.not_inside(pcomp.holes.not(pcomp).interacting(dnwell, 1..1).extents)
+dn3_2 = dnwell.inside((pcomp.holes.not(pcomp).covering(nat.or(ncomp).or(nwell).not_interacting(dnwell))))
+# Rule DN.3: Each DNWELL shall be directly surrounded by PCOMP guard ring tied to the P-substrate potential.
+logger.info("Executing rule DN.3")
+dn3_l1 = dn3_1.or(dn3_2)
+dn3_l1.output("DN.3", "DN.3 : Each DNWELL shall be directly surrounded by PCOMP guard ring tied to the P-substrate potential.")
+dn3_l1.forget
+
+dn3_1.forget
+dn3_2.forget
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/drc_bjt.drc b/rules/klayout/drc/rule_decks/drc_bjt.drc
new file mode 100644
index 0000000..f298cd7
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/drc_bjt.drc
@@ -0,0 +1,236 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (DRC_BJT) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+drc_bjt = polygons(127, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------DRC_BJT---------------------
+#================================================
+
+# Rule BJT.1: Min. DRC_BJT overlap of DNWELL for NPN BJT.
+logger.info("Executing rule BJT.1")
+bjt1_l1 = dnwell.interacting(drc_bjt).not(dnwell.inside(drc_bjt))
+bjt1_l1.output("BJT.1", "BJT.1 : Min. DRC_BJT overlap of DNWELL for NPN BJT.")
+bjt1_l1.forget
+
+# Rule BJT.2: Min. DRC_BJT overlap of PCOM in Psub.
+logger.info("Executing rule BJT.2")
+bjt2_l1 = pcomp.outside(nwell).outside(dnwell).interacting(drc_bjt).not(pcomp.outside(nwell).outside(dnwell).inside(drc_bjt))
+bjt2_l1.output("BJT.2", "BJT.2 : Min. DRC_BJT overlap of PCOM in Psub.")
+bjt2_l1.forget
+
+# Rule BJT.3: Minimum space of DRC_BJT layer to unrelated COMP. is 0.1µm
+logger.info("Executing rule BJT.3")
+bjt3_l1 = comp.outside(drc_bjt).separation(drc_bjt, 0.1.um, euclidian).polygons(0.001)
+bjt3_l1.output("BJT.3", "BJT.3 : Minimum space of DRC_BJT layer to unrelated COMP. : 0.1µm")
+bjt3_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/dualgate.drc b/rules/klayout/drc/rule_decks/dualgate.drc
new file mode 100644
index 0000000..bbbc14a
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/dualgate.drc
@@ -0,0 +1,288 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#---------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (DUALGATE) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------DUALGATE--------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule DV.1: Min. Dualgate enclose DNWELL. is 0.5µm
+logger.info("Executing rule DV.1")
+dv1_l1 = dualgate.enclosing(dnwell, 0.5.um, euclidian).polygons(0.001)
+dv1_l2 = dnwell.not_outside(dualgate).not(dualgate)
+dv1_l = dv1_l1.or(dv1_l2)
+dv1_l.output("DV.1", "DV.1 : Min. Dualgate enclose DNWELL. : 0.5µm")
+dv1_l1.forget
+dv1_l2.forget
+dv1_l.forget
+
+# Rule DV.2: Min. Dualgate Space. Merge if Space is less than this design rule. is 0.44µm
+logger.info("Executing rule DV.2")
+dv2_l1 = dualgate.space(0.44.um, euclidian).polygons(0.001)
+dv2_l1.output("DV.2", "DV.2 : Min. Dualgate Space. Merge if Space is less than this design rule. : 0.44µm")
+dv2_l1.forget
+
+# Rule DV.3: Min. Dualgate to COMP space [unrelated]. is 0.24µm
+logger.info("Executing rule DV.3")
+dv3_l1 = dualgate.separation(comp.outside(dualgate), 0.24.um, euclidian).polygons(0.001)
+dv3_l1.output("DV.3", "DV.3 : Min. Dualgate to COMP space [unrelated]. : 0.24µm")
+dv3_l1.forget
+
+# rule DV.4 is not a DRC check
+
+# Rule DV.5: Min. Dualgate width. is 0.7µm
+logger.info("Executing rule DV.5")
+dv5_l1 = dualgate.width(0.7.um, euclidian).polygons(0.001)
+dv5_l1.output("DV.5", "DV.5 : Min. Dualgate width. : 0.7µm")
+dv5_l1.forget
+
+comp_dv = comp.not(pcomp.outside(nwell))
+# Rule DV.6: Min. Dualgate enclose COMP (except substrate tap). is 0.24µm
+logger.info("Executing rule DV.6")
+dv6_l1 = dualgate.enclosing(comp_dv, 0.24.um, euclidian).polygons(0.001)
+dv6_l2 = comp_dv.not_outside(dualgate).not(dualgate)
+dv6_l = dv6_l1.or(dv6_l2)
+dv6_l.output("DV.6", "DV.6 : Min. Dualgate enclose COMP (except substrate tap). : 0.24µm")
+dv6_l1.forget
+dv6_l2.forget
+dv6_l.forget
+
+# Rule DV.7: COMP (except substrate tap) can not be partially overlapped by Dualgate.
+logger.info("Executing rule DV.7")
+dv7_l1 = dualgate.not_outside(comp_dv).not(dualgate.covering(comp_dv))
+dv7_l1.output("DV.7", "DV.7 : COMP (except substrate tap) can not be partially overlapped by Dualgate.")
+dv7_l1.forget
+
+comp_dv.forget
+# Rule DV.8: Min Dualgate enclose Poly2. is 0.4µm
+logger.info("Executing rule DV.8")
+dv8_l1 = dualgate.enclosing(poly2, 0.4.um, euclidian).polygons(0.001)
+dv8_l2 = poly2.not_outside(dualgate).not(dualgate)
+dv8_l = dv8_l1.or(dv8_l2)
+dv8_l.output("DV.8", "DV.8 : Min Dualgate enclose Poly2. : 0.4µm")
+dv8_l1.forget
+dv8_l2.forget
+dv8_l.forget
+
+# Rule DV.9: 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.
+logger.info("Executing rule DV.9")
+dv9_l1 = nwell.covering(pgate.and(dualgate)).covering(pgate.not_inside(v5_xtor).not_inside(dualgate))
+dv9_l1.output("DV.9", "DV.9 : 3.3V and 5V/6V PMOS cannot be sitting inside same NWELL.")
+dv9_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/dummy_exclude_layers.drc b/rules/klayout/drc/rule_decks/dummy_exclude_layers.drc
new file mode 100644
index 0000000..dc0e6f1
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/dummy_exclude_layers.drc
@@ -0,0 +1,239 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#---------------------------------------------------- GF 0.18um MCU DRC RULE DECK (DUMMY EXCLUDE LAYERS) -----------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+ndmy = polygons(111, 5 )
+pmndmy = polygons(152, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------DUMMY EXCLUDE LAYERS--------------
+#================================================
+
+# rule DE.1 is not a DRC check
+
+# Rule DE.2: Minimum NDMY or PMNDMY size (x or y dimension in um). is 0.8µm
+logger.info("Executing rule DE.2")
+de2_l1 = ndmy.or(pmndmy).width(0.8.um, euclidian).polygons(0.001)
+de2_l1.output("DE.2", "DE.2 : Minimum NDMY or PMNDMY size (x or y dimension in um). : 0.8µm")
+de2_l1.forget
+
+de3_ndmy_area = ndmy.with_area(15000.um, nil)
+# Rule DE.3: If size greater than 15000 um2 then two sides should not be greater than (um).
+logger.info("Executing rule DE.3")
+de3_l1 = de3_ndmy_area.edges.with_length(80.um, nil).not_interacting(de3_ndmy_area.edges.with_length(nil, 80.um))
+de3_l1.output("DE.3", "DE.3 : If size greater than 15000 um2 then two sides should not be greater than (um).")
+de3_l1.forget
+
+de3_ndmy_area.forget
+# Rule DE.4: Minimum NDMY to NDMY space (Merge if space is less). is 20µm
+logger.info("Executing rule DE.4")
+de4_l1 = ndmy.space(20.um, euclidian).polygons(0.001)
+de4_l1.output("DE.4", "DE.4 : Minimum NDMY to NDMY space (Merge if space is less). : 20µm")
+de4_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/efuse.drc b/rules/klayout/drc/rule_decks/efuse.drc
new file mode 100644
index 0000000..8acbda4
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/efuse.drc
@@ -0,0 +1,398 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (EFUSE) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+sab = polygons(49 , 0 )
+esd = polygons(24 , 0 )
+contact = polygons(33 , 0 )
+metal1 = polygons(34 , 0 )
+metal2 = polygons(36 , 0 )
+resistor = polygons(62 , 0 )
+lvs_source = polygons(100, 8 )
+plfuse = polygons(125, 5 )
+efuse_mk = polygons(80 , 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------EFUSE----------------------
+#================================================
+
+# Rule EF.01: Min. (Poly2 butt PLFUSE) within EFUSE_MK and Pplus.
+logger.info("Executing rule EF.01")
+ef01_l1 = poly2.or(plfuse).interacting(efuse_mk).not_inside(efuse_mk.and(pplus))
+ef01_l1.output("EF.01", "EF.01 : Min. (Poly2 butt PLFUSE) within EFUSE_MK and Pplus.")
+ef01_l1.forget
+
+# Rule EF.02: Min. Max. PLFUSE width. is 0.18µm
+logger.info("Executing rule EF.02")
+ef02_l1 = plfuse.drc(width != 0.18.um).extended(0, 0, 0.001, 0.001)
+ef02_l1.output("EF.02", "EF.02 : Min. Max. PLFUSE width. : 0.18µm")
+ef02_l1.forget
+
+# Rule EF.03: Min. Max. PLFUSE length. is 1.26µm
+logger.info("Executing rule EF.03")
+ef03_l1 = plfuse.edges.interacting(poly2.edges.and(plfuse.edges).centers(0, 0.95)).without_length(1.26.um).extended(0, 0, 0.001, 0.001)
+ef03_l1.output("EF.03", "EF.03 : Min. Max. PLFUSE length. : 1.26µm")
+ef03_l1.forget
+
+# Rule EF.04a: Min. Max. PLFUSE overlap Poly2 (coinciding permitted) and touch cathode and anode.
+logger.info("Executing rule EF.04a")
+ef04a_l1 = plfuse.not_in(plfuse.interacting(poly2.not(plfuse), 2, 2)).inside(efuse_mk).or(plfuse.not(poly2).inside(efuse_mk))
+ef04a_l1.output("EF.04a", "EF.04a : Min. Max. PLFUSE overlap Poly2 (coinciding permitted) and touch cathode and anode.")
+ef04a_l1.forget
+
+# Rule EF.04b: PLFUSE must be rectangular. is -µm
+logger.info("Executing rule EF.04b")
+ef04b_l1 = plfuse.non_rectangles
+ef04b_l1.output("EF.04b", "EF.04b : PLFUSE must be rectangular. : -µm")
+ef04b_l1.forget
+
+cathode = poly2.inside(efuse_mk).not(lvs_source.or(plfuse))
+# Rule EF.04c: Cathode Poly2 must be rectangular. is -µm
+logger.info("Executing rule EF.04c")
+ef04c_l1 = cathode.non_rectangles
+ef04c_l1.output("EF.04c", "EF.04c : Cathode Poly2 must be rectangular. : -µm")
+ef04c_l1.forget
+
+anode = poly2.and(lvs_source).inside(efuse_mk)
+# Rule EF.04d: Anode Poly2 must be rectangular. is -µm
+logger.info("Executing rule EF.04d")
+ef04d_l1 = anode.non_rectangles
+ef04d_l1.output("EF.04d", "EF.04d : Anode Poly2 must be rectangular. : -µm")
+ef04d_l1.forget
+
+# Rule EF.05: Min./Max. LVS_Source overlap Poly2 (at Anode).
+logger.info("Executing rule EF.05")
+ef05_l1 = poly2.not(plfuse).interacting(lvs_source).not(lvs_source).inside(efuse_mk).or(lvs_source.not(poly2).inside(efuse_mk))
+ef05_l1.output("EF.05", "EF.05 : Min./Max. LVS_Source overlap Poly2 (at Anode).")
+ef05_l1.forget
+
+cathode_width = cathode.edges.not_interacting(cathode.edges.interacting(plfuse)).or(cathode.edges.interacting(plfuse))
+# Rule EF.06: Min./Max. Cathode Poly2 width. is 2.26µm
+logger.info("Executing rule EF.06")
+ef06_l1 = cathode_width.without_length(2.26.um).extended(0, 0, 0.001, 0.001)
+ef06_l1.output("EF.06", "EF.06 : Min./Max. Cathode Poly2 width. : 2.26µm")
+ef06_l1.forget
+
+# Rule EF.07: Min./Max. Cathode Poly2 length. is 1.84µm
+logger.info("Executing rule EF.07")
+ef07_l1 = cathode.edges.not(cathode_width).without_length(1.84.um).extended(0, 0, 0.001, 0.001)
+ef07_l1.output("EF.07", "EF.07 : Min./Max. Cathode Poly2 length. : 1.84µm")
+ef07_l1.forget
+
+anode_width = anode.edges.not_interacting(anode.edges.interacting(plfuse)).or(anode.edges.interacting(plfuse))
+# Rule EF.08: Min./Max. Anode Poly2 width. is 1.06µm
+logger.info("Executing rule EF.08")
+ef08_l1 = anode_width.without_length(1.06.um).extended(0, 0, 0.001, 0.001)
+ef08_l1.output("EF.08", "EF.08 : Min./Max. Anode Poly2 width. : 1.06µm")
+ef08_l1.forget
+
+# Rule EF.09: Min./Max. Anode Poly2 length. is 2.43µm
+logger.info("Executing rule EF.09")
+ef09_l1 = anode.edges.not(anode_width).without_length(2.43.um).extended(0, 0, 0.001, 0.001)
+ef09_l1.output("EF.09", "EF.09 : Min./Max. Anode Poly2 length. : 2.43µm")
+ef09_l1.forget
+
+# Rule EF.10: Min. Cathode Poly2 to Poly2 space. is 0.26µm
+logger.info("Executing rule EF.10")
+ef10_l1 = cathode.space(0.26.um, euclidian).polygons(0.001)
+ef10_l1.output("EF.10", "EF.10 : Min. Cathode Poly2 to Poly2 space. : 0.26µm")
+ef10_l1.forget
+
+# Rule EF.11: Min. Anode Poly2 to Poly2 space. is 0.26µm
+logger.info("Executing rule EF.11")
+ef11_l1 = anode.space(0.26.um, euclidian).polygons(0.001)
+ef11_l1.output("EF.11", "EF.11 : Min. Anode Poly2 to Poly2 space. : 0.26µm")
+ef11_l1.forget
+
+cont_ef = contact.and(plfuse.inside(efuse_mk))
+# Rule EF.12: Min. Space of Cathode Contact to PLFUSE end.
+logger.info("Executing rule EF.12")
+ef12_l1 = plfuse.inside(efuse_mk).separation(contact.inside(cathode), 0.155.um).polygons(0.001).or(cont_ef)
+ef12_l1.output("EF.12", "EF.12 : Min. Space of Cathode Contact to PLFUSE end.")
+ef12_l1.forget
+
+# Rule EF.13: Min. Space of Anode Contact to PLFUSE end.
+logger.info("Executing rule EF.13")
+ef13_l1 = plfuse.inside(efuse_mk).separation(contact.inside(anode), 0.14.um).polygons(0.001).or(cont_ef)
+ef13_l1.output("EF.13", "EF.13 : Min. Space of Anode Contact to PLFUSE end.")
+ef13_l1.forget
+
+cont_ef.forget
+# Rule EF.14: Min. EFUSE_MK enclose LVS_Source.
+logger.info("Executing rule EF.14")
+ef14_l1 = lvs_source.not_outside(efuse_mk).not(efuse_mk)
+ef14_l1.output("EF.14", "EF.14 : Min. EFUSE_MK enclose LVS_Source.")
+ef14_l1.forget
+
+# Rule EF.15: NO Contact is allowed to touch PLFUSE.
+logger.info("Executing rule EF.15")
+ef15_l1 = plfuse.interacting(contact)
+ef15_l1.output("EF.15", "EF.15 : NO Contact is allowed to touch PLFUSE.")
+ef15_l1.forget
+
+# Rule EF.16a: Cathode must contain exact number of Contacts at each ends. is 4µm
+logger.info("Executing rule EF.16a")
+ef16a_l1 = cathode.not_covering(contact, 4, 4)
+ef16a_l1.output("EF.16a", "EF.16a : Cathode must contain exact number of Contacts at each ends. : 4µm")
+ef16a_l1.forget
+
+# Rule EF.16b: Anode must contain exact number of Contacts at each ends. is 4µm
+logger.info("Executing rule EF.16b")
+ef16b_l1 = anode.not_covering(contact, 4, 4)
+ef16b_l1.output("EF.16b", "EF.16b : Anode must contain exact number of Contacts at each ends. : 4µm")
+ef16b_l1.forget
+
+# Rule EF.17: Min. Space of EFUSE_MK to EFUSE_MK. is 0.26µm
+logger.info("Executing rule EF.17")
+ef17_l1 = efuse_mk.space(0.26.um, euclidian).polygons(0.001)
+ef17_l1.output("EF.17", "EF.17 : Min. Space of EFUSE_MK to EFUSE_MK. : 0.26µm")
+ef17_l1.forget
+
+# Rule EF.18: PLFUSE must sit on field oxide (NOT COMP), no cross with any COMP, Nplus, Pplus, ESD, SAB, Resistor, Metal1, Metal2.
+logger.info("Executing rule EF.18")
+ef18_l1 = plfuse.not_outside(comp.or(nplus).or(esd).or(sab).or(resistor).or(metal1).or(metal2))
+ef18_l1.output("EF.18", "EF.18 : PLFUSE must sit on field oxide (NOT COMP), no cross with any COMP, Nplus, Pplus, ESD, SAB, Resistor, Metal1, Metal2.")
+ef18_l1.forget
+
+# Rule EF.19: Min. PLFUSE space to Metal1, Metal2.
+logger.info("Executing rule EF.19")
+ef19_l1 = plfuse.not_outside(metal1.or(metal2))
+ef19_l1.output("EF.19", "EF.19 : Min. PLFUSE space to Metal1, Metal2.")
+ef19_l1.forget
+
+# Rule EF.20: Min. PLFUSE space to COMP, Nplus, Pplus, Resistor, ESD, SAB. is 2.73µm
+logger.info("Executing rule EF.20")
+ef20_l1 = plfuse.separation(comp.or(nplus).or(esd).or(sab).or(resistor), 2.73.um, euclidian).polygons(0.001)
+ef20_l1.output("EF.20", "EF.20 : Min. PLFUSE space to COMP, Nplus, Pplus, Resistor, ESD, SAB. : 2.73µm")
+ef20_l1.forget
+
+ef_21_fuse = poly2.interacting(plfuse).inside(efuse_mk.and(pplus)).extents.edges
+ef_21_anode = anode.edges.not_interacting(anode.edges.interacting(plfuse))
+ef_21_cathode = cathode.edges.not_interacting(cathode.edges.interacting(plfuse))
+# Rule EF.21: Min./Max. eFUSE Poly2 length. is 5.53µm
+logger.info("Executing rule EF.21")
+ef21_l1 = ef_21_fuse.not_interacting(ef_21_anode.or(ef_21_cathode).centers(0, 0.95)).without_length(5.53.um).extended(0, 0, 0.001, 0.001)
+ef21_l1.output("EF.21", "EF.21 : Min./Max. eFUSE Poly2 length. : 5.53µm")
+ef21_l1.forget
+
+ef_21_fuse.forget
+ef_21_anode.forget
+ef_21_cathode.forget
+# Rule EF.22a: Min./Max. Cathode Poly2 overlap with PLFUSE in width direction. is 1.04µm
+logger.info("Executing rule EF.22a")
+ef22a_l1 = cathode.edges.interacting(plfuse).not(plfuse.edges).without_length(1.04.um).extended(0, 0, 0.001, 0.001)
+ef22a_l1.output("EF.22a", "EF.22a : Min./Max. Cathode Poly2 overlap with PLFUSE in width direction. : 1.04µm")
+ef22a_l1.forget
+
+# Rule EF.22b: Min./Max. Anode Poly2 overlap with PLFUSE in width direction. is 0.44µm
+logger.info("Executing rule EF.22b")
+ef22b_l1 = anode.edges.interacting(plfuse).not(plfuse.edges).without_length(0.44.um).extended(0, 0, 0.001, 0.001)
+ef22b_l1.output("EF.22b", "EF.22b : Min./Max. Anode Poly2 overlap with PLFUSE in width direction. : 0.44µm")
+ef22b_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/esd.drc b/rules/klayout/drc/rule_decks/esd.drc
new file mode 100644
index 0000000..270dbc2
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/esd.drc
@@ -0,0 +1,306 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (ESD) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+esd = polygons(24 , 0 )
+lvs_io = polygons(119, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------ESD-----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule ESD.1: Minimum width of an ESD implant area. is 0.6µm
+logger.info("Executing rule ESD.1")
+esd1_l1 = esd.width(0.6.um, euclidian).polygons(0.001)
+esd1_l1.output("ESD.1", "ESD.1 : Minimum width of an ESD implant area. : 0.6µm")
+esd1_l1.forget
+
+# Rule ESD.2: Minimum space between two ESD implant areas. (Merge if the space is less than 0.6um). is 0.6µm
+logger.info("Executing rule ESD.2")
+esd2_l1 = esd.space(0.6.um, euclidian).polygons(0.001)
+esd2_l1.output("ESD.2", "ESD.2 : Minimum space between two ESD implant areas. (Merge if the space is less than 0.6um). : 0.6µm")
+esd2_l1.forget
+
+# Rule ESD.3a: Minimum space to NCOMP. is 0.6µm
+logger.info("Executing rule ESD.3a")
+esd3a_l1 = esd.separation(ncomp, 0.6.um, euclidian).polygons(0.001)
+esd3a_l1.output("ESD.3a", "ESD.3a : Minimum space to NCOMP. : 0.6µm")
+esd3a_l1.forget
+
+# Rule ESD.3b: Min/max space to a butted PCOMP.
+logger.info("Executing rule ESD.3b")
+esd3b_l1 = esd.not_outside(pcomp)
+esd3b_l1.output("ESD.3b", "ESD.3b : Min/max space to a butted PCOMP.")
+esd3b_l1.forget
+
+# Rule ESD.4a: Extension beyond NCOMP. is 0.24µm
+logger.info("Executing rule ESD.4a")
+esd4a_l1 = esd.edges.not_interacting(pcomp).enclosing(ncomp.edges, 0.24.um, euclidian).polygons(0.001)
+esd4a_l1.output("ESD.4a", "ESD.4a : Extension beyond NCOMP. : 0.24µm")
+esd4a_l1.forget
+
+# Rule ESD.4b: Minimum overlap of an ESD implant edge to a COMP. is 0.45µm
+logger.info("Executing rule ESD.4b")
+esd4b_l1 = esd.overlap(comp, 0.45.um, euclidian).polygons(0.001)
+esd4b_l1.output("ESD.4b", "ESD.4b : Minimum overlap of an ESD implant edge to a COMP. : 0.45µm")
+esd4b_l1.forget
+
+# Rule ESD.5a: Minimum ESD area (um2). is 0.49µm²
+logger.info("Executing rule ESD.5a")
+esd5a_l1 = esd.with_area(nil, 0.49.um)
+esd5a_l1.output("ESD.5a", "ESD.5a : Minimum ESD area (um2). : 0.49µm²")
+esd5a_l1.forget
+# Rule ESD.5b: Minimum field area enclosed by ESD implant (um2). is 0.49µm²
+logger.info("Executing rule ESD.5b")
+esd5b_l1 = esd.holes.with_area(nil, 0.49.um)
+esd5b_l1.output("ESD.5b", "ESD.5b : Minimum field area enclosed by ESD implant (um2). : 0.49µm²")
+esd5b_l1.forget
+# Rule ESD.6: Extension perpendicular to Poly2 gate. is 0.45µm
+logger.info("Executing rule ESD.6")
+esd6_l1 = esd.edges.enclosing(poly2.edges.interacting(tgate.edges), 0.45.um, projection).polygons(0.001)
+esd6_l1.output("ESD.6", "ESD.6 : Extension perpendicular to Poly2 gate. : 0.45µm")
+esd6_l1.forget
+
+# Rule ESD.7: No ESD implant inside PCOMP.
+logger.info("Executing rule ESD.7")
+esd7_l1 = esd.not_outside(pcomp)
+esd7_l1.output("ESD.7", "ESD.7 : No ESD implant inside PCOMP.")
+esd7_l1.forget
+
+# Rule ESD.8: Minimum space to Nplus/Pplus. is 0.3µm
+logger.info("Executing rule ESD.8")
+esd8_l1 = esd.separation(nplus.or(pplus), 0.3.um).polygons
+esd8_l1.output("ESD.8", "ESD.8 : Minimum space to Nplus/Pplus. : 0.3µm")
+esd8_l1.forget
+
+# Rule ESD.pl: Minimum gate length of 5V/6V gate NMOS. is 0.8µm
+logger.info("Executing rule ESD.pl")
+esdpl_l1 = poly2.interacting(esd).edges.and(tgate.edges).width(0.8.um, euclidian).polygons(0.001).overlapping(dualgate)
+esdpl_l1.output("ESD.pl", "ESD.pl : Minimum gate length of 5V/6V gate NMOS. : 0.8µm")
+esdpl_l1.forget
+
+# Rule ESD.9: ESD implant layer must be overlapped by Dualgate layer (as ESD implant option is only for 5V/6V devices).
+logger.info("Executing rule ESD.9")
+esd9_l1 = esd.not_inside(dualgate)
+esd9_l1.output("ESD.9", "ESD.9 : ESD implant layer must be overlapped by Dualgate layer (as ESD implant option is only for 5V/6V devices).")
+esd9_l1.forget
+
+# Rule ESD.10: LVS_IO shall be drawn covering I/O MOS active area by minimum overlap.
+logger.info("Executing rule ESD.10")
+esd10_l1 = comp.and(esd).not_outside(lvs_io).not(lvs_io)
+esd10_l1.output("ESD.10", "ESD.10 : LVS_IO shall be drawn covering I/O MOS active area by minimum overlap.")
+esd10_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/geometry_rules.drc b/rules/klayout/drc/rule_decks/geometry_rules.drc
new file mode 100644
index 0000000..37897b9
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/geometry_rules.drc
@@ -0,0 +1,750 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (GEOMETRY RULES) --------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+sab = polygons(49 , 0 )
+esd = polygons(24 , 0 )
+contact = polygons(33 , 0 )
+metal1 = polygons(34 , 0 )
+via1 = polygons(35 , 0 )
+metal2 = polygons(36 , 0 )
+via2 = polygons(38 , 0 )
+metal3 = polygons(42 , 0 )
+via3 = polygons(40 , 0 )
+metal4 = polygons(46 , 0 )
+via4 = polygons(41 , 0 )
+metal5 = polygons(81 , 0 )
+via5 = polygons(82 , 0 )
+metaltop = polygons(53 , 0 )
+pad = polygons(37 , 0 )
+resistor = polygons(62 , 0 )
+fhres = polygons(227, 0 )
+fusetop = polygons(75 , 0 )
+fusewindow_d = polygons(96 , 1 )
+polyfuse = polygons(220, 0 )
+mvsd = polygons(210, 0 )
+mvpsd = polygons(11 , 39)
+nat = polygons(5 , 0 )
+comp_dummy = polygons(22 , 4 )
+poly2_dummy = polygons(30 , 4 )
+metal1_dummy = polygons(34 , 4 )
+metal2_dummy = polygons(36 , 4 )
+metal3_dummy = polygons(42 , 4 )
+metal4_dummy = polygons(46 , 4 )
+metal5_dummy = polygons(81 , 4 )
+metaltop_dummy = polygons(53 , 4 )
+comp_label = polygons(22 , 10)
+poly2_label = polygons(30 , 10)
+metal1_label = polygons(34 , 10)
+metal2_label = polygons(36 , 10)
+metal3_label = polygons(42 , 10)
+metal4_label = polygons(46 , 10)
+metal5_label = polygons(81 , 10)
+metaltop_label = polygons(53 , 10)
+metal1_slot = polygons(34 , 3 )
+metal2_slot = polygons(36 , 3 )
+metal3_slot = polygons(42 , 3 )
+metal4_slot = polygons(46 , 3 )
+metal5_slot = polygons(81 , 3 )
+metaltop_slot = polygons(53 , 3 )
+ubmpperi = polygons(183, 0 )
+ubmparray = polygons(184, 0 )
+ubmeplate = polygons(185, 0 )
+schottky_diode = polygons(241, 0 )
+zener = polygons(178, 0 )
+res_mk = polygons(110, 5 )
+opc_drc = polygons(124, 5 )
+ndmy = polygons(111, 5 )
+pmndmy = polygons(152, 5 )
+v5_xtor = polygons(112, 1 )
+cap_mk = polygons(117, 5 )
+mos_cap_mk = polygons(166, 5 )
+ind_mk = polygons(151, 5 )
+diode_mk = polygons(115, 5 )
+drc_bjt = polygons(127, 5 )
+lvs_bjt = polygons(118, 5 )
+mim_l_mk = polygons(117, 10)
+latchup_mk = polygons(137, 5 )
+guard_ring_mk = polygons(167, 5 )
+otp_mk = polygons(173, 5 )
+mtpmark = polygons(122, 5 )
+neo_ee_mk = polygons(88 , 17)
+sramcore = polygons(108, 5 )
+lvs_rf = polygons(100, 5 )
+lvs_drain = polygons(100, 7 )
+ind_mk = polygons(151, 5 )
+hvpolyrs = polygons(123, 5 )
+lvs_io = polygons(119, 5 )
+probe_mk = polygons(13 , 17)
+esd_mk = polygons(24 , 5 )
+lvs_source = polygons(100, 8 )
+well_diode_mk = polygons(153, 51)
+ldmos_xtor = polygons(226, 0 )
+plfuse = polygons(125, 5 )
+efuse_mk = polygons(80 , 5 )
+mcell_feol_mk = polygons(11 , 17)
+ymtp_mk = polygons(86 , 17)
+dev_wf_mk = polygons(128, 17)
+metal1_blk = polygons(34 , 5 )
+metal2_blk = polygons(36 , 5 )
+metal3_blk = polygons(42 , 5 )
+metal4_blk = polygons(46 , 5 )
+metal5_blk = polygons(81 , 5 )
+metalt_blk = polygons(53 , 5 )
+pr_bndry = polygons(0 , 0 )
+mdiode = polygons(116, 5 )
+metal1_res = polygons(110, 11)
+metal2_res = polygons(110, 12)
+metal3_res = polygons(110, 13)
+metal4_res = polygons(110, 14)
+metal5_res = polygons(110, 15)
+metal6_res = polygons(110, 16)
+border = polygons(63 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#-----------------GEOMETRY RULES-----------------
+#================================================
+
+if OFFGRID
+logger.info("OFFGRID-ANGLES section")
+
+logger.info("Executing rule comp_OFFGRID")
+comp.ongrid(0.005).output("comp_OFFGRID", "OFFGRID : OFFGRID vertex on comp")
+comp.with_angle(0 .. 45).output("comp_angle", "ACUTE : non 45 degree angle comp")
+
+logger.info("Executing rule dnwell_OFFGRID")
+dnwell.ongrid(0.005).output("dnwell_OFFGRID", "OFFGRID : OFFGRID vertex on dnwell")
+dnwell.with_angle(0 .. 45).output("dnwell_angle", "ACUTE : non 45 degree angle dnwell")
+
+logger.info("Executing rule nwell_OFFGRID")
+nwell.ongrid(0.005).output("nwell_OFFGRID", "OFFGRID : OFFGRID vertex on nwell")
+nwell.with_angle(0 .. 45).output("nwell_angle", "ACUTE : non 45 degree angle nwell")
+
+logger.info("Executing rule lvpwell_OFFGRID")
+lvpwell.ongrid(0.005).output("lvpwell_OFFGRID", "OFFGRID : OFFGRID vertex on lvpwell")
+lvpwell.with_angle(0 .. 45).output("lvpwell_angle", "ACUTE : non 45 degree angle lvpwell")
+
+logger.info("Executing rule dualgate_OFFGRID")
+dualgate.ongrid(0.005).output("dualgate_OFFGRID", "OFFGRID : OFFGRID vertex on dualgate")
+dualgate.with_angle(0 .. 45).output("dualgate_angle", "ACUTE : non 45 degree angle dualgate")
+
+logger.info("Executing rule poly2_OFFGRID")
+poly2.ongrid(0.005).output("poly2_OFFGRID", "OFFGRID : OFFGRID vertex on poly2")
+poly2.with_angle(0 .. 45).output("poly2_angle", "ACUTE : non 45 degree angle poly2")
+
+logger.info("Executing rule nplus_OFFGRID")
+nplus.ongrid(0.005).output("nplus_OFFGRID", "OFFGRID : OFFGRID vertex on nplus")
+nplus.with_angle(0 .. 45).output("nplus_angle", "ACUTE : non 45 degree angle nplus")
+
+logger.info("Executing rule pplus_OFFGRID")
+pplus.ongrid(0.005).output("pplus_OFFGRID", "OFFGRID : OFFGRID vertex on pplus")
+pplus.with_angle(0 .. 45).output("pplus_angle", "ACUTE : non 45 degree angle pplus")
+
+logger.info("Executing rule sab_OFFGRID")
+sab.ongrid(0.005).output("sab_OFFGRID", "OFFGRID : OFFGRID vertex on sab")
+sab.with_angle(0 .. 45).output("sab_angle", "ACUTE : non 45 degree angle sab")
+
+logger.info("Executing rule esd_OFFGRID")
+esd.ongrid(0.005).output("esd_OFFGRID", "OFFGRID : OFFGRID vertex on esd")
+esd.with_angle(0 .. 45).output("esd_angle", "ACUTE : non 45 degree angle esd")
+
+logger.info("Executing rule contact_OFFGRID")
+contact.ongrid(0.005).output("contact_OFFGRID", "OFFGRID : OFFGRID vertex on contact")
+contact.with_angle(0 .. 45).output("contact_angle", "ACUTE : non 45 degree angle contact")
+
+logger.info("Executing rule metal1_OFFGRID")
+metal1.ongrid(0.005).output("metal1_OFFGRID", "OFFGRID : OFFGRID vertex on metal1")
+metal1.with_angle(0 .. 45).output("metal1_angle", "ACUTE : non 45 degree angle metal1")
+
+logger.info("Executing rule via1_OFFGRID")
+via1.ongrid(0.005).output("via1_OFFGRID", "OFFGRID : OFFGRID vertex on via1")
+via1.with_angle(0 .. 45).output("via1_angle", "ACUTE : non 45 degree angle via1")
+
+logger.info("Executing rule metal2_OFFGRID")
+metal2.ongrid(0.005).output("metal2_OFFGRID", "OFFGRID : OFFGRID vertex on metal2")
+metal2.with_angle(0 .. 45).output("metal2_angle", "ACUTE : non 45 degree angle metal2")
+
+logger.info("Executing rule via2_OFFGRID")
+via2.ongrid(0.005).output("via2_OFFGRID", "OFFGRID : OFFGRID vertex on via2")
+via2.with_angle(0 .. 45).output("via2_angle", "ACUTE : non 45 degree angle via2")
+
+logger.info("Executing rule metal3_OFFGRID")
+metal3.ongrid(0.005).output("metal3_OFFGRID", "OFFGRID : OFFGRID vertex on metal3")
+metal3.with_angle(0 .. 45).output("metal3_angle", "ACUTE : non 45 degree angle metal3")
+
+logger.info("Executing rule via3_OFFGRID")
+via3.ongrid(0.005).output("via3_OFFGRID", "OFFGRID : OFFGRID vertex on via3")
+via3.with_angle(0 .. 45).output("via3_angle", "ACUTE : non 45 degree angle via3")
+
+logger.info("Executing rule metal4_OFFGRID")
+metal4.ongrid(0.005).output("metal4_OFFGRID", "OFFGRID : OFFGRID vertex on metal4")
+metal4.with_angle(0 .. 45).output("metal4_angle", "ACUTE : non 45 degree angle metal4")
+
+logger.info("Executing rule via4_OFFGRID")
+via4.ongrid(0.005).output("via4_OFFGRID", "OFFGRID : OFFGRID vertex on via4")
+via4.with_angle(0 .. 45).output("via4_angle", "ACUTE : non 45 degree angle via4")
+
+logger.info("Executing rule metal5_OFFGRID")
+metal5.ongrid(0.005).output("metal5_OFFGRID", "OFFGRID : OFFGRID vertex on metal5")
+metal5.with_angle(0 .. 45).output("metal5_angle", "ACUTE : non 45 degree angle metal5")
+
+logger.info("Executing rule via5_OFFGRID")
+via5.ongrid(0.005).output("via5_OFFGRID", "OFFGRID : OFFGRID vertex on via5")
+via5.with_angle(0 .. 45).output("via5_angle", "ACUTE : non 45 degree angle via5")
+
+logger.info("Executing rule metaltop_OFFGRID")
+metaltop.ongrid(0.005).output("metaltop_OFFGRID", "OFFGRID : OFFGRID vertex on metaltop")
+metaltop.with_angle(0 .. 45).output("metaltop_angle", "ACUTE : non 45 degree angle metaltop")
+
+logger.info("Executing rule pad_OFFGRID")
+pad.ongrid(0.005).output("pad_OFFGRID", "OFFGRID : OFFGRID vertex on pad")
+pad.with_angle(0 .. 45).output("pad_angle", "ACUTE : non 45 degree angle pad")
+
+logger.info("Executing rule resistor_OFFGRID")
+resistor.ongrid(0.005).output("resistor_OFFGRID", "OFFGRID : OFFGRID vertex on resistor")
+resistor.with_angle(0 .. 45).output("resistor_angle", "ACUTE : non 45 degree angle resistor")
+
+logger.info("Executing rule fhres_OFFGRID")
+fhres.ongrid(0.005).output("fhres_OFFGRID", "OFFGRID : OFFGRID vertex on fhres")
+fhres.with_angle(0 .. 45).output("fhres_angle", "ACUTE : non 45 degree angle fhres")
+
+logger.info("Executing rule fusetop_OFFGRID")
+fusetop.ongrid(0.005).output("fusetop_OFFGRID", "OFFGRID : OFFGRID vertex on fusetop")
+fusetop.with_angle(0 .. 45).output("fusetop_angle", "ACUTE : non 45 degree angle fusetop")
+
+logger.info("Executing rule fusewindow_d_OFFGRID")
+fusewindow_d.ongrid(0.005).output("fusewindow_d_OFFGRID", "OFFGRID : OFFGRID vertex on fusewindow_d")
+fusewindow_d.with_angle(0 .. 45).output("fusewindow_d_angle", "ACUTE : non 45 degree angle fusewindow_d")
+
+logger.info("Executing rule polyfuse_OFFGRID")
+polyfuse.ongrid(0.005).output("polyfuse_OFFGRID", "OFFGRID : OFFGRID vertex on polyfuse")
+polyfuse.with_angle(0 .. 45).output("polyfuse_angle", "ACUTE : non 45 degree angle polyfuse")
+
+logger.info("Executing rule mvsd_OFFGRID")
+mvsd.ongrid(0.005).output("mvsd_OFFGRID", "OFFGRID : OFFGRID vertex on mvsd")
+mvsd.with_angle(0 .. 45).output("mvsd_angle", "ACUTE : non 45 degree angle mvsd")
+
+logger.info("Executing rule mvpsd_OFFGRID")
+mvpsd.ongrid(0.005).output("mvpsd_OFFGRID", "OFFGRID : OFFGRID vertex on mvpsd")
+mvpsd.with_angle(0 .. 45).output("mvpsd_angle", "ACUTE : non 45 degree angle mvpsd")
+
+logger.info("Executing rule nat_OFFGRID")
+nat.ongrid(0.005).output("nat_OFFGRID", "OFFGRID : OFFGRID vertex on nat")
+nat.with_angle(0 .. 45).output("nat_angle", "ACUTE : non 45 degree angle nat")
+
+logger.info("Executing rule comp_dummy_OFFGRID")
+comp_dummy.ongrid(0.005).output("comp_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on comp_dummy")
+comp_dummy.with_angle(0 .. 45).output("comp_dummy_angle", "ACUTE : non 45 degree angle comp_dummy")
+
+logger.info("Executing rule poly2_dummy_OFFGRID")
+poly2_dummy.ongrid(0.005).output("poly2_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on poly2_dummy")
+poly2_dummy.with_angle(0 .. 45).output("poly2_dummy_angle", "ACUTE : non 45 degree angle poly2_dummy")
+
+logger.info("Executing rule metal1_dummy_OFFGRID")
+metal1_dummy.ongrid(0.005).output("metal1_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metal1_dummy")
+metal1_dummy.with_angle(0 .. 45).output("metal1_dummy_angle", "ACUTE : non 45 degree angle metal1_dummy")
+
+logger.info("Executing rule metal2_dummy_OFFGRID")
+metal2_dummy.ongrid(0.005).output("metal2_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metal2_dummy")
+metal2_dummy.with_angle(0 .. 45).output("metal2_dummy_angle", "ACUTE : non 45 degree angle metal2_dummy")
+
+logger.info("Executing rule metal3_dummy_OFFGRID")
+metal3_dummy.ongrid(0.005).output("metal3_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metal3_dummy")
+metal3_dummy.with_angle(0 .. 45).output("metal3_dummy_angle", "ACUTE : non 45 degree angle metal3_dummy")
+
+logger.info("Executing rule metal4_dummy_OFFGRID")
+metal4_dummy.ongrid(0.005).output("metal4_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metal4_dummy")
+metal4_dummy.with_angle(0 .. 45).output("metal4_dummy_angle", "ACUTE : non 45 degree angle metal4_dummy")
+
+logger.info("Executing rule metal5_dummy_OFFGRID")
+metal5_dummy.ongrid(0.005).output("metal5_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metal5_dummy")
+metal5_dummy.with_angle(0 .. 45).output("metal5_dummy_angle", "ACUTE : non 45 degree angle metal5_dummy")
+
+logger.info("Executing rule metaltop_dummy_OFFGRID")
+metaltop_dummy.ongrid(0.005).output("metaltop_dummy_OFFGRID", "OFFGRID : OFFGRID vertex on metaltop_dummy")
+metaltop_dummy.with_angle(0 .. 45).output("metaltop_dummy_angle", "ACUTE : non 45 degree angle metaltop_dummy")
+
+logger.info("Executing rule comp_label_OFFGRID")
+comp_label.ongrid(0.005).output("comp_label_OFFGRID", "OFFGRID : OFFGRID vertex on comp_label")
+comp_label.with_angle(0 .. 45).output("comp_label_angle", "ACUTE : non 45 degree angle comp_label")
+
+logger.info("Executing rule poly2_label_OFFGRID")
+poly2_label.ongrid(0.005).output("poly2_label_OFFGRID", "OFFGRID : OFFGRID vertex on poly2_label")
+poly2_label.with_angle(0 .. 45).output("poly2_label_angle", "ACUTE : non 45 degree angle poly2_label")
+
+logger.info("Executing rule metal1_label_OFFGRID")
+metal1_label.ongrid(0.005).output("metal1_label_OFFGRID", "OFFGRID : OFFGRID vertex on metal1_label")
+metal1_label.with_angle(0 .. 45).output("metal1_label_angle", "ACUTE : non 45 degree angle metal1_label")
+
+logger.info("Executing rule metal2_label_OFFGRID")
+metal2_label.ongrid(0.005).output("metal2_label_OFFGRID", "OFFGRID : OFFGRID vertex on metal2_label")
+metal2_label.with_angle(0 .. 45).output("metal2_label_angle", "ACUTE : non 45 degree angle metal2_label")
+
+logger.info("Executing rule metal3_label_OFFGRID")
+metal3_label.ongrid(0.005).output("metal3_label_OFFGRID", "OFFGRID : OFFGRID vertex on metal3_label")
+metal3_label.with_angle(0 .. 45).output("metal3_label_angle", "ACUTE : non 45 degree angle metal3_label")
+
+logger.info("Executing rule metal4_label_OFFGRID")
+metal4_label.ongrid(0.005).output("metal4_label_OFFGRID", "OFFGRID : OFFGRID vertex on metal4_label")
+metal4_label.with_angle(0 .. 45).output("metal4_label_angle", "ACUTE : non 45 degree angle metal4_label")
+
+logger.info("Executing rule metal5_label_OFFGRID")
+metal5_label.ongrid(0.005).output("metal5_label_OFFGRID", "OFFGRID : OFFGRID vertex on metal5_label")
+metal5_label.with_angle(0 .. 45).output("metal5_label_angle", "ACUTE : non 45 degree angle metal5_label")
+
+logger.info("Executing rule metaltop_label_OFFGRID")
+metaltop_label.ongrid(0.005).output("metaltop_label_OFFGRID", "OFFGRID : OFFGRID vertex on metaltop_label")
+metaltop_label.with_angle(0 .. 45).output("metaltop_label_angle", "ACUTE : non 45 degree angle metaltop_label")
+
+logger.info("Executing rule metal1_slot_OFFGRID")
+metal1_slot.ongrid(0.005).output("metal1_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metal1_slot")
+metal1_slot.with_angle(0 .. 45).output("metal1_slot_angle", "ACUTE : non 45 degree angle metal1_slot")
+
+logger.info("Executing rule metal2_slot_OFFGRID")
+metal2_slot.ongrid(0.005).output("metal2_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metal2_slot")
+metal2_slot.with_angle(0 .. 45).output("metal2_slot_angle", "ACUTE : non 45 degree angle metal2_slot")
+
+logger.info("Executing rule metal3_slot_OFFGRID")
+metal3_slot.ongrid(0.005).output("metal3_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metal3_slot")
+metal3_slot.with_angle(0 .. 45).output("metal3_slot_angle", "ACUTE : non 45 degree angle metal3_slot")
+
+logger.info("Executing rule metal4_slot_OFFGRID")
+metal4_slot.ongrid(0.005).output("metal4_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metal4_slot")
+metal4_slot.with_angle(0 .. 45).output("metal4_slot_angle", "ACUTE : non 45 degree angle metal4_slot")
+
+logger.info("Executing rule metal5_slot_OFFGRID")
+metal5_slot.ongrid(0.005).output("metal5_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metal5_slot")
+metal5_slot.with_angle(0 .. 45).output("metal5_slot_angle", "ACUTE : non 45 degree angle metal5_slot")
+
+logger.info("Executing rule metaltop_slot_OFFGRID")
+metaltop_slot.ongrid(0.005).output("metaltop_slot_OFFGRID", "OFFGRID : OFFGRID vertex on metaltop_slot")
+metaltop_slot.with_angle(0 .. 45).output("metaltop_slot_angle", "ACUTE : non 45 degree angle metaltop_slot")
+
+logger.info("Executing rule ubmpperi_OFFGRID")
+ubmpperi.ongrid(0.005).output("ubmpperi_OFFGRID", "OFFGRID : OFFGRID vertex on ubmpperi")
+ubmpperi.with_angle(0 .. 45).output("ubmpperi_angle", "ACUTE : non 45 degree angle ubmpperi")
+
+logger.info("Executing rule ubmparray_OFFGRID")
+ubmparray.ongrid(0.005).output("ubmparray_OFFGRID", "OFFGRID : OFFGRID vertex on ubmparray")
+ubmparray.with_angle(0 .. 45).output("ubmparray_angle", "ACUTE : non 45 degree angle ubmparray")
+
+logger.info("Executing rule ubmeplate_OFFGRID")
+ubmeplate.ongrid(0.005).output("ubmeplate_OFFGRID", "OFFGRID : OFFGRID vertex on ubmeplate")
+ubmeplate.with_angle(0 .. 45).output("ubmeplate_angle", "ACUTE : non 45 degree angle ubmeplate")
+
+logger.info("Executing rule schottky_diode_OFFGRID")
+schottky_diode.ongrid(0.005).output("schottky_diode_OFFGRID", "OFFGRID : OFFGRID vertex on schottky_diode")
+schottky_diode.with_angle(0 .. 45).output("schottky_diode_angle", "ACUTE : non 45 degree angle schottky_diode")
+
+logger.info("Executing rule zener_OFFGRID")
+zener.ongrid(0.005).output("zener_OFFGRID", "OFFGRID : OFFGRID vertex on zener")
+zener.with_angle(0 .. 45).output("zener_angle", "ACUTE : non 45 degree angle zener")
+
+logger.info("Executing rule res_mk_OFFGRID")
+res_mk.ongrid(0.005).output("res_mk_OFFGRID", "OFFGRID : OFFGRID vertex on res_mk")
+res_mk.with_angle(0 .. 45).output("res_mk_angle", "ACUTE : non 45 degree angle res_mk")
+
+logger.info("Executing rule opc_drc_OFFGRID")
+opc_drc.ongrid(0.005).output("opc_drc_OFFGRID", "OFFGRID : OFFGRID vertex on opc_drc")
+opc_drc.with_angle(0 .. 45).output("opc_drc_angle", "ACUTE : non 45 degree angle opc_drc")
+
+logger.info("Executing rule ndmy_OFFGRID")
+ndmy.ongrid(0.005).output("ndmy_OFFGRID", "OFFGRID : OFFGRID vertex on ndmy")
+ndmy.with_angle(0 .. 45).output("ndmy_angle", "ACUTE : non 45 degree angle ndmy")
+
+logger.info("Executing rule pmndmy_OFFGRID")
+pmndmy.ongrid(0.005).output("pmndmy_OFFGRID", "OFFGRID : OFFGRID vertex on pmndmy")
+pmndmy.with_angle(0 .. 45).output("pmndmy_angle", "ACUTE : non 45 degree angle pmndmy")
+
+logger.info("Executing rule v5_xtor_OFFGRID")
+v5_xtor.ongrid(0.005).output("v5_xtor_OFFGRID", "OFFGRID : OFFGRID vertex on v5_xtor")
+v5_xtor.with_angle(0 .. 45).output("v5_xtor_angle", "ACUTE : non 45 degree angle v5_xtor")
+
+logger.info("Executing rule cap_mk_OFFGRID")
+cap_mk.ongrid(0.005).output("cap_mk_OFFGRID", "OFFGRID : OFFGRID vertex on cap_mk")
+cap_mk.with_angle(0 .. 45).output("cap_mk_angle", "ACUTE : non 45 degree angle cap_mk")
+
+logger.info("Executing rule mos_cap_mk_OFFGRID")
+mos_cap_mk.ongrid(0.005).output("mos_cap_mk_OFFGRID", "OFFGRID : OFFGRID vertex on mos_cap_mk")
+mos_cap_mk.with_angle(0 .. 45).output("mos_cap_mk_angle", "ACUTE : non 45 degree angle mos_cap_mk")
+
+logger.info("Executing rule ind_mk_OFFGRID")
+ind_mk.ongrid(0.005).output("ind_mk_OFFGRID", "OFFGRID : OFFGRID vertex on ind_mk")
+ind_mk.with_angle(0 .. 45).output("ind_mk_angle", "ACUTE : non 45 degree angle ind_mk")
+
+logger.info("Executing rule diode_mk_OFFGRID")
+diode_mk.ongrid(0.005).output("diode_mk_OFFGRID", "OFFGRID : OFFGRID vertex on diode_mk")
+diode_mk.with_angle(0 .. 45).output("diode_mk_angle", "ACUTE : non 45 degree angle diode_mk")
+
+logger.info("Executing rule drc_bjt_OFFGRID")
+drc_bjt.ongrid(0.005).output("drc_bjt_OFFGRID", "OFFGRID : OFFGRID vertex on drc_bjt")
+drc_bjt.with_angle(0 .. 45).output("drc_bjt_angle", "ACUTE : non 45 degree angle drc_bjt")
+
+logger.info("Executing rule lvs_bjt_OFFGRID")
+lvs_bjt.ongrid(0.005).output("lvs_bjt_OFFGRID", "OFFGRID : OFFGRID vertex on lvs_bjt")
+lvs_bjt.with_angle(0 .. 45).output("lvs_bjt_angle", "ACUTE : non 45 degree angle lvs_bjt")
+
+logger.info("Executing rule mim_l_mk_OFFGRID")
+mim_l_mk.ongrid(0.005).output("mim_l_mk_OFFGRID", "OFFGRID : OFFGRID vertex on mim_l_mk")
+mim_l_mk.with_angle(0 .. 45).output("mim_l_mk_angle", "ACUTE : non 45 degree angle mim_l_mk")
+
+logger.info("Executing rule latchup_mk_OFFGRID")
+latchup_mk.ongrid(0.005).output("latchup_mk_OFFGRID", "OFFGRID : OFFGRID vertex on latchup_mk")
+latchup_mk.with_angle(0 .. 45).output("latchup_mk_angle", "ACUTE : non 45 degree angle latchup_mk")
+
+logger.info("Executing rule guard_ring_mk_OFFGRID")
+guard_ring_mk.ongrid(0.005).output("guard_ring_mk_OFFGRID", "OFFGRID : OFFGRID vertex on guard_ring_mk")
+guard_ring_mk.with_angle(0 .. 45).output("guard_ring_mk_angle", "ACUTE : non 45 degree angle guard_ring_mk")
+
+logger.info("Executing rule otp_mk_OFFGRID")
+otp_mk.ongrid(0.005).output("otp_mk_OFFGRID", "OFFGRID : OFFGRID vertex on otp_mk")
+otp_mk.with_angle(0 .. 45).output("otp_mk_angle", "ACUTE : non 45 degree angle otp_mk")
+
+logger.info("Executing rule mtpmark_OFFGRID")
+mtpmark.ongrid(0.005).output("mtpmark_OFFGRID", "OFFGRID : OFFGRID vertex on mtpmark")
+mtpmark.with_angle(0 .. 45).output("mtpmark_angle", "ACUTE : non 45 degree angle mtpmark")
+
+logger.info("Executing rule neo_ee_mk_OFFGRID")
+neo_ee_mk.ongrid(0.005).output("neo_ee_mk_OFFGRID", "OFFGRID : OFFGRID vertex on neo_ee_mk")
+neo_ee_mk.with_angle(0 .. 45).output("neo_ee_mk_angle", "ACUTE : non 45 degree angle neo_ee_mk")
+
+logger.info("Executing rule sramcore_OFFGRID")
+sramcore.ongrid(0.005).output("sramcore_OFFGRID", "OFFGRID : OFFGRID vertex on sramcore")
+sramcore.with_angle(0 .. 45).output("sramcore_angle", "ACUTE : non 45 degree angle sramcore")
+
+logger.info("Executing rule lvs_rf_OFFGRID")
+lvs_rf.ongrid(0.005).output("lvs_rf_OFFGRID", "OFFGRID : OFFGRID vertex on lvs_rf")
+lvs_rf.with_angle(0 .. 45).output("lvs_rf_angle", "ACUTE : non 45 degree angle lvs_rf")
+
+logger.info("Executing rule lvs_drain_OFFGRID")
+lvs_drain.ongrid(0.005).output("lvs_drain_OFFGRID", "OFFGRID : OFFGRID vertex on lvs_drain")
+lvs_drain.with_angle(0 .. 45).output("lvs_drain_angle", "ACUTE : non 45 degree angle lvs_drain")
+
+logger.info("Executing rule ind_mk_OFFGRID")
+ind_mk.ongrid(0.005).output("ind_mk_OFFGRID", "OFFGRID : OFFGRID vertex on ind_mk")
+ind_mk.with_angle(0 .. 45).output("ind_mk_angle", "ACUTE : non 45 degree angle ind_mk")
+
+logger.info("Executing rule hvpolyrs_OFFGRID")
+hvpolyrs.ongrid(0.005).output("hvpolyrs_OFFGRID", "OFFGRID : OFFGRID vertex on hvpolyrs")
+hvpolyrs.with_angle(0 .. 45).output("hvpolyrs_angle", "ACUTE : non 45 degree angle hvpolyrs")
+
+logger.info("Executing rule lvs_io_OFFGRID")
+lvs_io.ongrid(0.005).output("lvs_io_OFFGRID", "OFFGRID : OFFGRID vertex on lvs_io")
+lvs_io.with_angle(0 .. 45).output("lvs_io_angle", "ACUTE : non 45 degree angle lvs_io")
+
+logger.info("Executing rule probe_mk_OFFGRID")
+probe_mk.ongrid(0.005).output("probe_mk_OFFGRID", "OFFGRID : OFFGRID vertex on probe_mk")
+probe_mk.with_angle(0 .. 45).output("probe_mk_angle", "ACUTE : non 45 degree angle probe_mk")
+
+logger.info("Executing rule esd_mk_OFFGRID")
+esd_mk.ongrid(0.005).output("esd_mk_OFFGRID", "OFFGRID : OFFGRID vertex on esd_mk")
+esd_mk.with_angle(0 .. 45).output("esd_mk_angle", "ACUTE : non 45 degree angle esd_mk")
+
+logger.info("Executing rule lvs_source_OFFGRID")
+lvs_source.ongrid(0.005).output("lvs_source_OFFGRID", "OFFGRID : OFFGRID vertex on lvs_source")
+lvs_source.with_angle(0 .. 45).output("lvs_source_angle", "ACUTE : non 45 degree angle lvs_source")
+
+logger.info("Executing rule well_diode_mk_OFFGRID")
+well_diode_mk.ongrid(0.005).output("well_diode_mk_OFFGRID", "OFFGRID : OFFGRID vertex on well_diode_mk")
+well_diode_mk.with_angle(0 .. 45).output("well_diode_mk_angle", "ACUTE : non 45 degree angle well_diode_mk")
+
+logger.info("Executing rule ldmos_xtor_OFFGRID")
+ldmos_xtor.ongrid(0.005).output("ldmos_xtor_OFFGRID", "OFFGRID : OFFGRID vertex on ldmos_xtor")
+ldmos_xtor.with_angle(0 .. 45).output("ldmos_xtor_angle", "ACUTE : non 45 degree angle ldmos_xtor")
+
+logger.info("Executing rule plfuse_OFFGRID")
+plfuse.ongrid(0.005).output("plfuse_OFFGRID", "OFFGRID : OFFGRID vertex on plfuse")
+plfuse.with_angle(0 .. 45).output("plfuse_angle", "ACUTE : non 45 degree angle plfuse")
+
+logger.info("Executing rule efuse_mk_OFFGRID")
+efuse_mk.ongrid(0.005).output("efuse_mk_OFFGRID", "OFFGRID : OFFGRID vertex on efuse_mk")
+efuse_mk.with_angle(0 .. 45).output("efuse_mk_angle", "ACUTE : non 45 degree angle efuse_mk")
+
+logger.info("Executing rule mcell_feol_mk_OFFGRID")
+mcell_feol_mk.ongrid(0.005).output("mcell_feol_mk_OFFGRID", "OFFGRID : OFFGRID vertex on mcell_feol_mk")
+mcell_feol_mk.with_angle(0 .. 45).output("mcell_feol_mk_angle", "ACUTE : non 45 degree angle mcell_feol_mk")
+
+logger.info("Executing rule ymtp_mk_OFFGRID")
+ymtp_mk.ongrid(0.005).output("ymtp_mk_OFFGRID", "OFFGRID : OFFGRID vertex on ymtp_mk")
+ymtp_mk.with_angle(0 .. 45).output("ymtp_mk_angle", "ACUTE : non 45 degree angle ymtp_mk")
+
+logger.info("Executing rule dev_wf_mk_OFFGRID")
+dev_wf_mk.ongrid(0.005).output("dev_wf_mk_OFFGRID", "OFFGRID : OFFGRID vertex on dev_wf_mk")
+dev_wf_mk.with_angle(0 .. 45).output("dev_wf_mk_angle", "ACUTE : non 45 degree angle dev_wf_mk")
+
+logger.info("Executing rule metal1_blk_OFFGRID")
+metal1_blk.ongrid(0.005).output("metal1_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metal1_blk")
+metal1_blk.with_angle(0 .. 45).output("metal1_blk_angle", "ACUTE : non 45 degree angle metal1_blk")
+
+logger.info("Executing rule metal2_blk_OFFGRID")
+metal2_blk.ongrid(0.005).output("metal2_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metal2_blk")
+metal2_blk.with_angle(0 .. 45).output("metal2_blk_angle", "ACUTE : non 45 degree angle metal2_blk")
+
+logger.info("Executing rule metal3_blk_OFFGRID")
+metal3_blk.ongrid(0.005).output("metal3_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metal3_blk")
+metal3_blk.with_angle(0 .. 45).output("metal3_blk_angle", "ACUTE : non 45 degree angle metal3_blk")
+
+logger.info("Executing rule metal4_blk_OFFGRID")
+metal4_blk.ongrid(0.005).output("metal4_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metal4_blk")
+metal4_blk.with_angle(0 .. 45).output("metal4_blk_angle", "ACUTE : non 45 degree angle metal4_blk")
+
+logger.info("Executing rule metal5_blk_OFFGRID")
+metal5_blk.ongrid(0.005).output("metal5_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metal5_blk")
+metal5_blk.with_angle(0 .. 45).output("metal5_blk_angle", "ACUTE : non 45 degree angle metal5_blk")
+
+logger.info("Executing rule metalt_blk_OFFGRID")
+metalt_blk.ongrid(0.005).output("metalt_blk_OFFGRID", "OFFGRID : OFFGRID vertex on metalt_blk")
+metalt_blk.with_angle(0 .. 45).output("metalt_blk_angle", "ACUTE : non 45 degree angle metalt_blk")
+
+logger.info("Executing rule pr_bndry_OFFGRID")
+pr_bndry.ongrid(0.005).output("pr_bndry_OFFGRID", "OFFGRID : OFFGRID vertex on pr_bndry")
+pr_bndry.with_angle(0 .. 45).output("pr_bndry_angle", "ACUTE : non 45 degree angle pr_bndry")
+
+logger.info("Executing rule mdiode_OFFGRID")
+mdiode.ongrid(0.005).output("mdiode_OFFGRID", "OFFGRID : OFFGRID vertex on mdiode")
+mdiode.with_angle(0 .. 45).output("mdiode_angle", "ACUTE : non 45 degree angle mdiode")
+
+logger.info("Executing rule metal1_res_OFFGRID")
+metal1_res.ongrid(0.005).output("metal1_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal1_res")
+metal1_res.with_angle(0 .. 45).output("metal1_res_angle", "ACUTE : non 45 degree angle metal1_res")
+
+logger.info("Executing rule metal2_res_OFFGRID")
+metal2_res.ongrid(0.005).output("metal2_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal2_res")
+metal2_res.with_angle(0 .. 45).output("metal2_res_angle", "ACUTE : non 45 degree angle metal2_res")
+
+logger.info("Executing rule metal3_res_OFFGRID")
+metal3_res.ongrid(0.005).output("metal3_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal3_res")
+metal3_res.with_angle(0 .. 45).output("metal3_res_angle", "ACUTE : non 45 degree angle metal3_res")
+
+logger.info("Executing rule metal4_res_OFFGRID")
+metal4_res.ongrid(0.005).output("metal4_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal4_res")
+metal4_res.with_angle(0 .. 45).output("metal4_res_angle", "ACUTE : non 45 degree angle metal4_res")
+
+logger.info("Executing rule metal5_res_OFFGRID")
+metal5_res.ongrid(0.005).output("metal5_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal5_res")
+metal5_res.with_angle(0 .. 45).output("metal5_res_angle", "ACUTE : non 45 degree angle metal5_res")
+
+logger.info("Executing rule metal6_res_OFFGRID")
+metal6_res.ongrid(0.005).output("metal6_res_OFFGRID", "OFFGRID : OFFGRID vertex on metal6_res")
+metal6_res.with_angle(0 .. 45).output("metal6_res_angle", "ACUTE : non 45 degree angle metal6_res")
+
+logger.info("Executing rule border_OFFGRID")
+border.ongrid(0.005).output("border_OFFGRID", "OFFGRID : OFFGRID vertex on border")
+border.with_angle(0 .. 45).output("border_angle", "ACUTE : non 45 degree angle border")
+
+end #OFFGRID-ANGLES
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/h_poly_resistor.drc b/rules/klayout/drc/rule_decks/h_poly_resistor.drc
new file mode 100644
index 0000000..e7535fc
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/h_poly_resistor.drc
@@ -0,0 +1,321 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (H POLY RESISTOR) -------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+resistor = polygons(62 , 0 )
+res_mk = polygons(110, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------H POLY RESISTOR-----------------
+#================================================
+
+hres_poly = poly2.interacting(pplus).interacting(sab).interacting(res_mk).interacting(resistor)
+hres1_poly = poly2.interacting(pplus).interacting(sab).interacting(res_mk)
+# Rule HRES.1: Minimum space. Note : Merge if the spacing is less than 0.4 um. is 0.4µm
+logger.info("Executing rule HRES.1")
+hres1_l1 = resistor.interacting(hres1_poly).space(0.4.um, euclidian).polygons(0.001)
+hres1_l1.output("HRES.1", "HRES.1 : Minimum space. Note : Merge if the spacing is less than 0.4 um. : 0.4µm")
+hres1_l1.forget
+
+# Rule HRES.2: Minimum width of Poly2 resistor. is 1µm
+logger.info("Executing rule HRES.2")
+hres2_l1 = hres_poly.width(1.um, euclidian).polygons(0.001)
+hres2_l1.output("HRES.2", "HRES.2 : Minimum width of Poly2 resistor. : 1µm")
+hres2_l1.forget
+
+# Rule HRES.3: Minimum space between Poly2 resistors. is 0.4µm
+logger.info("Executing rule HRES.3")
+hres3_l1 = hres_poly.space(0.4.um, euclidian).polygons(0.001)
+hres3_l1.output("HRES.3", "HRES.3 : Minimum space between Poly2 resistors. : 0.4µm")
+hres3_l1.forget
+
+# Rule HRES.4: Minimum RESISTOR overlap of Poly2 resistor. is 0.4µm
+logger.info("Executing rule HRES.4")
+hres4_l1 = resistor.enclosing(hres_poly, 0.4.um, euclidian).polygons(0.001)
+hres4_l2 = hres_poly.not_outside(resistor).not(resistor)
+hres4_l = hres4_l1.or(hres4_l2)
+hres4_l.output("HRES.4", "HRES.4 : Minimum RESISTOR overlap of Poly2 resistor. : 0.4µm")
+hres4_l1.forget
+hres4_l2.forget
+hres4_l.forget
+
+# Rule HRES.5: Minimum RESISTOR space to unrelated Poly2. is 0.3µm
+logger.info("Executing rule HRES.5")
+hres5_l1 = resistor.interacting(hres1_poly).separation(poly2.not_interacting(sab), 0.3.um, euclidian).polygons(0.001)
+hres5_l1.output("HRES.5", "HRES.5 : Minimum RESISTOR space to unrelated Poly2. : 0.3µm")
+hres5_l1.forget
+
+# Rule HRES.6: Minimum RESISTOR space to COMP.
+logger.info("Executing rule HRES.6")
+hres6_l1 = resistor.interacting(hres1_poly).separation(comp, 0.3.um, euclidian).polygons(0.001).or(comp.not_outside(resistor.interacting(poly2.interacting(pplus).interacting(sab).interacting(res_mk))))
+hres6_l1.output("HRES.6", "HRES.6 : Minimum RESISTOR space to COMP.")
+hres6_l1.forget
+
+hres1_poly.forget
+# Rule HRES.7: Minimum Pplus overlap of contact on Poly2 resistor. is 0.2µm
+logger.info("Executing rule HRES.7")
+hres7_l1 = pplus.enclosing(contact.inside(hres_poly), 0.2.um, euclidian).polygons(0.001)
+hres7_l2 = contact.inside(hres_poly).not_outside(pplus).not(pplus)
+hres7_l = hres7_l1.or(hres7_l2)
+hres7_l.output("HRES.7", "HRES.7 : Minimum Pplus overlap of contact on Poly2 resistor. : 0.2µm")
+hres7_l1.forget
+hres7_l2.forget
+hres7_l.forget
+
+# Rule HRES.8: Space from salicide block to contact on Poly2 resistor.
+logger.info("Executing rule HRES.8")
+hres8_l1 = contact.inside(hres_poly).separation(sab,0.22.um).polygons(0.001).or(contact.inside(hres_poly).interacting(sab))
+hres8_l1.output("HRES.8", "HRES.8 : Space from salicide block to contact on Poly2 resistor.")
+hres8_l1.forget
+
+hres9_sab = sab.interacting(pplus).interacting(res_mk).interacting(resistor)
+hres9_clear_sab = hres9_sab.not(hres_poly)
+hres9_bad_inside_edge = hres9_sab.edges.inside_part(hres_poly).extended(0,0,0.001,0.001).interacting(hres9_clear_sab, 1, 1)
+hres9_sab_hole = hres9_sab.holes.and(hres_poly)
+# Rule HRES.9: Minimum salicide block overlap of Poly2 resistor in width direction.
+logger.info("Executing rule HRES.9")
+hres9_l1 = hres9_sab.enclosing(hres_poly, 0.28.um, euclidian).polygons(0.001).or(hres9_bad_inside_edge).or(hres9_sab_hole)
+hres9_l1.output("HRES.9", "HRES.9 : Minimum salicide block overlap of Poly2 resistor in width direction.")
+hres9_l1.forget
+
+hres9_sab.forget
+hres9_clear_sab.forget
+hres9_bad_inside_edge.forget
+hres9_sab_hole.forget
+pplus1_hres10 = pplus.and(sab).drc(width != 0.1.um)
+pplus2_hres10 = pplus.not_overlapping(sab).edges
+# Rule HRES.10: Minimum & maximum Pplus overlap of SAB.
+logger.info("Executing rule HRES.10")
+hres10_l1 = pplus1_hres10.or(pplus2_hres10).extended(0, 0, 0.001, 0.001).interacting(hres_poly)
+hres10_l1.output("HRES.10", "HRES.10 : Minimum & maximum Pplus overlap of SAB.")
+hres10_l1.forget
+
+pplus1_hres10.forget
+pplus2_hres10.forget
+# rule HRES.11 is not a DRC check
+
+mk_hres12a = res_mk.edges.not(poly2.not(pplus).and(sab).edges).inside_part(poly2)
+# Rule HRES.12a: P type Poly2 resistor (high sheet rho) shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by Pplus space) and width covering the width of Poly2.
+logger.info("Executing rule HRES.12a")
+hres12a_l1 = res_mk.interacting(resistor).interacting(mk_hres12a)
+hres12a_l1.output("HRES.12a", "HRES.12a : P type Poly2 resistor (high sheet rho) shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by Pplus space) and width covering the width of Poly2. ")
+hres12a_l1.forget
+
+mk_hres12a.forget
+hres12b = res_mk.with_area(15000.01.um,nil).in(res_mk.interacting(res_mk.edges.with_length(80.01.um,nil)))
+# Rule HRES.12b: If the size of single RES_MK mark layer is greater than 15000 um2 and both side (X and Y) are greater than 80 um. Then the minimum spacing to adjacent RES_MK layer. is 20µm
+logger.info("Executing rule HRES.12b")
+hres12b_l1 = res_mk.interacting(hres_poly).drc(separation(hres12b) < 20.um).polygons(0.001)
+hres12b_l1.output("HRES.12b", "HRES.12b : If the size of single RES_MK mark layer is greater than 15000 um2 and both side (X and Y) are greater than 80 um. Then the minimum spacing to adjacent RES_MK layer. : 20µm")
+hres12b_l1.forget
+
+hres12b.forget
+hres_poly.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/lvpwell.drc b/rules/klayout/drc/rule_decks/lvpwell.drc
new file mode 100644
index 0000000..2bc38a5
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/lvpwell.drc
@@ -0,0 +1,426 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (LVPWELL) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+sab = polygons(49 , 0 )
+res_mk = polygons(110, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+#================================================
+#------------- LAYERS CONNECTIONS ---------------
+#================================================
+
+if CONNECTIVITY_RULES
+
+ logger.info("Construct connectivity for the design.")
+
+ connect(dnwell, ncomp)
+ connect(ncomp, contact)
+ connect(pcomp, contact)
+ connect(lvpwell, ncomp)
+ connect(nwell, ncomp)
+ connect(natcompsd, contact)
+ connect(mvsd, ncomp)
+ connect(mvpsd, pcomp)
+ connect(contact, metal1)
+ connect(metal1, via1)
+ connect(via1, metal2)
+ connect(metal2, via2)
+ connect(via2, metal3)
+ connect(metal3, via3)
+ connect(via3, metal4)
+ connect(metal4, via4)
+ connect(via4, metal5)
+ connect(metal5, via5)
+ connect(via5, metaltop)
+
+end #CONNECTIVITY_RULES
+
+#================================================
+#------------ PRE-DEFINED FUNCTIONS -------------
+#================================================
+
+def conn_space(layer,conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer.space(conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
+ singularity_errors = layer.space(0.001.um)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
+ return connected_output, unconnected_output
+end
+
+def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons(0.001)
+ return connected_output, unconnected_output
+end
+
+# === IMPLICIT EXTRACTION ===
+if CONNECTIVITY_RULES
+ logger.info("Connectivity rules enabled, Netlist object will be generated.")
+ netlist
+end #CONNECTIVITY_RULES
+
+# === LAYOUT EXTENT ===
+CHIP = extent.sized(0.0)
+
+logger.info("Total area of the design is #{CHIP.area()} um^2.")
+
+
+
+#================================================
+#--------------------LVPWELL---------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule LPW.1_3.3V: Min. LVPWELL Width. is 0.6µm
+logger.info("Executing rule LPW.1_3.3V")
+lpw1_l1 = lvpwell.width(0.6.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+lpw1_l1.output("LPW.1_3.3V", "LPW.1_3.3V : Min. LVPWELL Width. : 0.6µm")
+lpw1_l1.forget
+
+# Rule LPW.1_5V: Min. LVPWELL Width. is 0.74µm
+logger.info("Executing rule LPW.1_5V")
+lpw1_l1 = lvpwell.width(0.74.um, euclidian).polygons(0.001).overlapping(dualgate)
+lpw1_l1.output("LPW.1_5V", "LPW.1_5V : Min. LVPWELL Width. : 0.74µm")
+lpw1_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_lvpwell_3p3v, unconnected_lvpwell_3p3v = conn_space(lvpwell, 0.86, 1.4, euclidian)
+
+connected_lvpwell_5p0v, unconnected_lvpwell_5p0v = conn_space(lvpwell, 0.86, 1.7, euclidian)
+
+# Rule LPW.2a_3.3V: Min. LVPWELL to LVWELL Space (Inside DNWELL) [Different potential]. is 1.4µm
+logger.info("Executing rule LPW.2a_3.3V")
+lpw2a_l1 = unconnected_lvpwell_3p3v.not_interacting(v5_xtor).not_interacting(dualgate)
+lpw2a_l1.output("LPW.2a_3.3V", "LPW.2a_3.3V : Min. LVPWELL to LVWELL Space (Inside DNWELL) [Different potential]. : 1.4µm")
+lpw2a_l1.forget
+
+# Rule LPW.2a_5V: Min. LVPWELL to LVPWELL Space (Inside DNWELL) [Different potential]. is 1.7µm
+logger.info("Executing rule LPW.2a_5V")
+lpw2a_l1 = unconnected_lvpwell_5p0v.overlapping(dualgate)
+lpw2a_l1.output("LPW.2a_5V", "LPW.2a_5V : Min. LVPWELL to LVPWELL Space (Inside DNWELL) [Different potential]. : 1.7µm")
+lpw2a_l1.forget
+
+# Rule LPW.2b_3.3V: Min. LVPWELL to LVPWELL Space [Equi potential]. is 0.86µm
+logger.info("Executing rule LPW.2b_3.3V")
+lpw2b_l1 = connected_lvpwell_3p3v.not_interacting(v5_xtor).not_interacting(dualgate)
+lpw2b_l1.output("LPW.2b_3.3V", "LPW.2b_3.3V : Min. LVPWELL to LVPWELL Space [Equi potential]. : 0.86µm")
+lpw2b_l1.forget
+
+# Rule LPW.2b_5V: Min. LVPWELL to LVPWELL Space [Equi potential]. is 0.86µm
+logger.info("Executing rule LPW.2b_5V")
+lpw2b_l1 = connected_lvpwell_5p0v.overlapping(dualgate)
+lpw2b_l1.output("LPW.2b_5V", "LPW.2b_5V : Min. LVPWELL to LVPWELL Space [Equi potential]. : 0.86µm")
+lpw2b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule LPW.2a_3.3V_: Min. LVPWELL to LVWELL Space (Inside DNWELL) [Different potential]. is 1.4µm
+logger.info("Executing rule LPW.2a_3.3V_")
+lpw2a_l1 = lvpwell.isolated(1.4.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+lpw2a_l1.output("LPW.2a_3.3V_", "LPW.2a_3.3V_ : Min. LVPWELL to LVWELL Space (Inside DNWELL) [Different potential]. : 1.4µm")
+lpw2a_l1.forget
+
+# Rule LPW.2a_5V_: Min. LVPWELL to LVPWELL Space (Inside DNWELL) [Different potential]. is 1.7µm
+logger.info("Executing rule LPW.2a_5V_")
+lpw2a_l1 = lvpwell.isolated(1.7.um, euclidian).polygons(0.001).overlapping(dualgate)
+lpw2a_l1.output("LPW.2a_5V_", "LPW.2a_5V_ : Min. LVPWELL to LVPWELL Space (Inside DNWELL) [Different potential]. : 1.7µm")
+lpw2a_l1.forget
+
+end #CONNECTIVITY_RULES
+
+# Rule LPW.3_3.3V: Min. DNWELL enclose LVPWELL. is 2.5µm
+logger.info("Executing rule LPW.3_3.3V")
+lpw3_l1 = dnwell.enclosing(lvpwell, 2.5.um, euclidian).polygons(0.001)
+lpw3_l2 = lvpwell.not_outside(dnwell).not(dnwell)
+lpw3_l = lpw3_l1.or(lpw3_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+lpw3_l.output("LPW.3_3.3V", "LPW.3_3.3V : Min. DNWELL enclose LVPWELL. : 2.5µm")
+lpw3_l1.forget
+lpw3_l2.forget
+lpw3_l.forget
+
+# Rule LPW.3_5V: Min. DNWELL enclose LVPWELL. is 2.5µm
+logger.info("Executing rule LPW.3_5V")
+lpw3_l1 = dnwell.enclosing(lvpwell, 2.5.um, euclidian).polygons(0.001)
+lpw3_l2 = lvpwell.not_outside(dnwell).not(dnwell)
+lpw3_l = lpw3_l1.or(lpw3_l2).overlapping(dualgate)
+lpw3_l.output("LPW.3_5V", "LPW.3_5V : Min. DNWELL enclose LVPWELL. : 2.5µm")
+lpw3_l1.forget
+lpw3_l2.forget
+lpw3_l.forget
+
+# rule LPW.4_3.3V is not a DRC check
+
+# rule LPW.4_5V is not a DRC check
+
+# Rule LPW.5_3.3V: LVPWELL resistors must be enclosed by DNWELL.
+logger.info("Executing rule LPW.5_3.3V")
+lpw5_l1 = lvpwell.inside(res_mk).not_inside(dnwell).not_interacting(v5_xtor).not_interacting(dualgate)
+lpw5_l1.output("LPW.5_3.3V", "LPW.5_3.3V : LVPWELL resistors must be enclosed by DNWELL.")
+lpw5_l1.forget
+
+# Rule LPW.5_5V: LVPWELL resistors must be enclosed by DNWELL.
+logger.info("Executing rule LPW.5_5V")
+lpw5_l1 = lvpwell.inside(res_mk).not_inside(dnwell).overlapping(dualgate)
+lpw5_l1.output("LPW.5_5V", "LPW.5_5V : LVPWELL resistors must be enclosed by DNWELL.")
+lpw5_l1.forget
+
+# Rule LPW.11: Min. (LVPWELL outside DNWELL) space to DNWELL. is 1.5µm
+logger.info("Executing rule LPW.11")
+lpw11_l1 = lvpwell.outside(dnwell).separation(dnwell, 1.5.um, euclidian).polygons(0.001)
+lpw11_l1.output("LPW.11", "LPW.11 : Min. (LVPWELL outside DNWELL) space to DNWELL. : 1.5µm")
+lpw11_l1.forget
+
+# Rule LPW.12: LVPWELL cannot overlap with Nwell.
+logger.info("Executing rule LPW.12")
+lpw12_l1 = lvpwell.not_outside(nwell)
+lpw12_l1.output("LPW.12", "LPW.12 : LVPWELL cannot overlap with Nwell.")
+lpw12_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/lvs_bjt.drc b/rules/klayout/drc/rule_decks/lvs_bjt.drc
new file mode 100644
index 0000000..007c667
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/lvs_bjt.drc
@@ -0,0 +1,228 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (LVS_BJT) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvs_bjt = polygons(118, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------LVS_BJT---------------------
+#================================================
+
+vnpn_e = ncomp.interacting(lvs_bjt).inside(dnwell)
+vpnp_e = pcomp.inside(nwell).interacting(lvs_bjt)
+# Rule LVS_BJT.1: Minimum LVS_BJT enclosure of NPN or PNP Emitter COMP layers
+logger.info("Executing rule LVS_BJT.1")
+lvs_l1 = vnpn_e.or(vpnp_e).not_inside(lvs_bjt)
+lvs_l1.output("LVS_BJT.1", "LVS_BJT.1 : Minimum LVS_BJT enclosure of NPN or PNP Emitter COMP layers")
+lvs_l1.forget
+
+vnpn_e.forget
+vpnp_e.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/mcell.drc b/rules/klayout/drc/rule_decks/mcell.drc
new file mode 100644
index 0000000..4c4cf4d
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/mcell.drc
@@ -0,0 +1,238 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (MCELL) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+mcell_feol_mk = polygons(11 , 17)
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------MCELL----------------------
+#================================================
+
+# Rule MC.1: min. mcell width is 0.4µm
+logger.info("Executing rule MC.1")
+mc1_l1 = mcell_feol_mk.width(0.4.um, euclidian).polygons(0.001)
+mc1_l1.output("MC.1", "MC.1 : min. mcell width : 0.4µm")
+mc1_l1.forget
+
+# Rule MC.2: min. mcell spacing is 0.4µm
+logger.info("Executing rule MC.2")
+mc2_l1 = mcell_feol_mk.space(0.4.um, euclidian).polygons(0.001)
+mc2_l1.output("MC.2", "MC.2 : min. mcell spacing : 0.4µm")
+mc2_l1.forget
+
+# Rule MC.3: Minimum Mcell area is 0.35µm²
+logger.info("Executing rule MC.3")
+mc3_l1 = mcell_feol_mk.with_area(nil, 0.35.um)
+mc3_l1.output("MC.3", "MC.3 : Minimum Mcell area : 0.35µm²")
+mc3_l1.forget
+# Rule MC.4: Minimum area enclosed by Mcell is 0.35µm²
+logger.info("Executing rule MC.4")
+mc4_l1 = mcell_feol_mk.holes.with_area(nil, 0.35.um)
+mc4_l1.output("MC.4", "MC.4 : Minimum area enclosed by Mcell : 0.35µm²")
+mc4_l1.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metal1.drc b/rules/klayout/drc/rule_decks/metal1.drc
new file mode 100644
index 0000000..eb1489e
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metal1.drc
@@ -0,0 +1,247 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METAL1) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal1 = polygons(34 , 0 )
+sramcore = polygons(108, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------METAL1---------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule M1.1: min. metal1 width is 0.23µm
+logger.info("Executing rule M1.1")
+m11_l1 = metal1.not(sramcore).width(0.23.um, euclidian).polygons(0.001)
+m11_l1.output("M1.1", "M1.1 : min. metal1 width : 0.23µm")
+m11_l1.forget
+
+# Rule M1.2a: min. metal1 spacing is 0.23µm
+logger.info("Executing rule M1.2a")
+m12a_l1 = metal1.space(0.23.um, euclidian).polygons(0.001)
+m12a_l1.output("M1.2a", "M1.2a : min. metal1 spacing : 0.23µm")
+m12a_l1.forget
+
+# Rule M1.2b: Space to wide Metal1 (length & width > 10um) is 0.3µm
+logger.info("Executing rule M1.2b")
+m12b_l1 = metal1.separation(metal1.not_interacting(metal1.edges.with_length(nil, 10.um)), 0.3.um, euclidian).polygons(0.001)
+m12b_l1.output("M1.2b", "M1.2b : Space to wide Metal1 (length & width > 10um) : 0.3µm")
+m12b_l1.forget
+
+# Rule M1.3: Minimum Metal1 area is 0.1444µm²
+logger.info("Executing rule M1.3")
+m13_l1 = metal1.with_area(nil, 0.1444.um)
+m13_l1.output("M1.3", "M1.3 : Minimum Metal1 area : 0.1444µm²")
+m13_l1.forget
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metal2.drc b/rules/klayout/drc/rule_decks/metal2.drc
new file mode 100644
index 0000000..621d706
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metal2.drc
@@ -0,0 +1,246 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METAL2) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal2 = polygons(36 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------METAL2---------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule M2.1: min. metal2 width is 0.28µm
+logger.info("Executing rule M2.1")
+m21_l1 = metal2.width(0.28.um, euclidian).polygons(0.001)
+m21_l1.output("M2.1", "M2.1 : min. metal2 width : 0.28µm")
+m21_l1.forget
+
+# Rule M2.2a: min. metal2 spacing is 0.28µm
+logger.info("Executing rule M2.2a")
+m22a_l1 = metal2.space(0.28.um, euclidian).polygons(0.001)
+m22a_l1.output("M2.2a", "M2.2a : min. metal2 spacing : 0.28µm")
+m22a_l1.forget
+
+# Rule M2.2b: Space to wide Metal2 (length & width > 10um) is 0.3µm
+logger.info("Executing rule M2.2b")
+m22b_l1 = metal2.separation(metal2.not_interacting(metal2.edges.with_length(nil, 10.um)), 0.3.um, euclidian).polygons(0.001)
+m22b_l1.output("M2.2b", "M2.2b : Space to wide Metal2 (length & width > 10um) : 0.3µm")
+m22b_l1.forget
+
+# Rule M2.3: Minimum metal2 area is 0.1444µm²
+logger.info("Executing rule M2.3")
+m23_l1 = metal2.with_area(nil, 0.1444.um)
+m23_l1.output("M2.3", "M2.3 : Minimum metal2 area : 0.1444µm²")
+m23_l1.forget
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metal3.drc b/rules/klayout/drc/rule_decks/metal3.drc
new file mode 100644
index 0000000..7f58aee
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metal3.drc
@@ -0,0 +1,246 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METAL3) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal3 = polygons(42 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------METAL3---------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule M3.1: min. metal3 width is 0.28µm
+logger.info("Executing rule M3.1")
+m31_l1 = metal3.width(0.28.um, euclidian).polygons(0.001)
+m31_l1.output("M3.1", "M3.1 : min. metal3 width : 0.28µm")
+m31_l1.forget
+
+# Rule M3.2a: min. metal3 spacing is 0.28µm
+logger.info("Executing rule M3.2a")
+m32a_l1 = metal3.space(0.28.um, euclidian).polygons(0.001)
+m32a_l1.output("M3.2a", "M3.2a : min. metal3 spacing : 0.28µm")
+m32a_l1.forget
+
+# Rule M3.2b: Space to wide Metal3 (length & width > 10um) is 0.3µm
+logger.info("Executing rule M3.2b")
+m32b_l1 = metal3.separation(metal3.not_interacting(metal3.edges.with_length(nil, 10.um)), 0.3.um, euclidian).polygons(0.001)
+m32b_l1.output("M3.2b", "M3.2b : Space to wide Metal3 (length & width > 10um) : 0.3µm")
+m32b_l1.forget
+
+# Rule M3.3: Minimum metal3 area is 0.1444µm²
+logger.info("Executing rule M3.3")
+m33_l1 = metal3.with_area(nil, 0.1444.um)
+m33_l1.output("M3.3", "M3.3 : Minimum metal3 area : 0.1444µm²")
+m33_l1.forget
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metal4.drc b/rules/klayout/drc/rule_decks/metal4.drc
new file mode 100644
index 0000000..a267756
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metal4.drc
@@ -0,0 +1,246 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METAL4) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal4 = polygons(46 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------METAL4---------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule M4.1: min. metal4 width is 0.28µm
+logger.info("Executing rule M4.1")
+m41_l1 = metal4.width(0.28.um, euclidian).polygons(0.001)
+m41_l1.output("M4.1", "M4.1 : min. metal4 width : 0.28µm")
+m41_l1.forget
+
+# Rule M4.2a: min. metal4 spacing is 0.28µm
+logger.info("Executing rule M4.2a")
+m42a_l1 = metal4.space(0.28.um, euclidian).polygons(0.001)
+m42a_l1.output("M4.2a", "M4.2a : min. metal4 spacing : 0.28µm")
+m42a_l1.forget
+
+# Rule M4.2b: Space to wide Metal4 (length & width > 10um) is 0.3µm
+logger.info("Executing rule M4.2b")
+m42b_l1 = metal4.separation(metal4.not_interacting(metal4.edges.with_length(nil, 10.um)), 0.3.um, euclidian).polygons(0.001)
+m42b_l1.output("M4.2b", "M4.2b : Space to wide Metal4 (length & width > 10um) : 0.3µm")
+m42b_l1.forget
+
+# Rule M4.3: Minimum metal4 area is 0.1444µm²
+logger.info("Executing rule M4.3")
+m43_l1 = metal4.with_area(nil, 0.1444.um)
+m43_l1.output("M4.3", "M4.3 : Minimum metal4 area : 0.1444µm²")
+m43_l1.forget
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metal5.drc b/rules/klayout/drc/rule_decks/metal5.drc
new file mode 100644
index 0000000..e6b9502
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metal5.drc
@@ -0,0 +1,246 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METAL5) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal5 = polygons(81 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------METAL5---------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule M5.1: min. metal5 width is 0.28µm
+logger.info("Executing rule M5.1")
+m51_l1 = metal5.width(0.28.um, euclidian).polygons(0.001)
+m51_l1.output("M5.1", "M5.1 : min. metal5 width : 0.28µm")
+m51_l1.forget
+
+# Rule M5.2a: min. metal5 spacing is 0.28µm
+logger.info("Executing rule M5.2a")
+m52a_l1 = metal5.space(0.28.um, euclidian).polygons(0.001)
+m52a_l1.output("M5.2a", "M5.2a : min. metal5 spacing : 0.28µm")
+m52a_l1.forget
+
+# Rule M5.2b: Space to wide Metal5 (length & width > 10um) is 0.3µm
+logger.info("Executing rule M5.2b")
+m52b_l1 = metal5.separation(metal5.not_interacting(metal5.edges.with_length(nil, 10.um)), 0.3.um, euclidian).polygons(0.001)
+m52b_l1.output("M5.2b", "M5.2b : Space to wide Metal5 (length & width > 10um) : 0.3µm")
+m52b_l1.forget
+
+# Rule M5.3: Minimum metal5 area is 0.1444µm²
+logger.info("Executing rule M5.3")
+m53_l1 = metal5.with_area(nil, 0.1444.um)
+m53_l1.output("M5.3", "M5.3 : Minimum metal5 area : 0.1444µm²")
+m53_l1.forget
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/metaltop.drc b/rules/klayout/drc/rule_decks/metaltop.drc
new file mode 100644
index 0000000..625c580
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/metaltop.drc
@@ -0,0 +1,381 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#---------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (METALTOP) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metaltop = polygons(53 , 0 )
+fusetop = polygons(75 , 0 )
+guard_ring_mk = polygons(167, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#------------- METAL LEVEL SWITCHES -------------
+#================================================
+
+
+if METAL_LEVEL == "6LM"
+ via4 = polygons(41 , 0 )
+ metal5 = polygons(81 , 0 )
+ via5 = polygons(82 , 0 )
+ metaltop = polygons(53 , 0 )
+ top_via = via5
+ topmin1_via = via4
+ top_metal = metaltop
+ topmin1_metal = metal5
+elsif METAL_LEVEL == "5LM"
+ via3 = polygons(40 , 0 )
+ metal4 = polygons(46 , 0 )
+ via4 = polygons(41 , 0 )
+ metal5 = polygons(81 , 0 )
+ top_via = via4
+ topmin1_via = via3
+ top_metal = metal5
+ topmin1_metal = metal4
+elsif METAL_LEVEL == "4LM"
+ via2 = polygons(38 , 0 )
+ metal3 = polygons(42 , 0 )
+ via3 = polygons(40 , 0 )
+ metal4 = polygons(46 , 0 )
+ top_via = via3
+ topmin1_via = via2
+ top_metal = metal4
+ topmin1_metal = metal3
+elsif METAL_LEVEL == "3LM"
+ via1 = polygons(35 , 0 )
+ metal2 = polygons(36 , 0 )
+ via2 = polygons(38 , 0 )
+ metal3 = polygons(42 , 0 )
+ top_via = via2
+ topmin1_via = via1
+ top_metal = metal3
+ topmin1_metal = metal2
+elsif METAL_LEVEL == "2LM"
+ metal1 = polygons(34 , 0 )
+ via1 = polygons(35 , 0 )
+ metal2 = polygons(36 , 0 )
+ top_via = via1
+ topmin1_via = via1
+ top_metal = metal2
+ topmin1_metal = metal1
+end #METAL_LEVEL
+
+
+#================================================
+#--------------------METALTOP--------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+if METAL_TOP == "6K"
+logger.info("MetalTop thickness 6k section")
+
+# Rule MT.1: min. metaltop width is 0.36µm
+logger.info("Executing rule MT.1")
+mt1_l1 = metaltop.width(0.36.um, euclidian).polygons(0.001)
+mt1_l1.output("MT.1", "MT.1 : min. metaltop width : 0.36µm")
+mt1_l1.forget
+
+# Rule MT.2a: min. metaltop spacing is 0.38µm
+logger.info("Executing rule MT.2a")
+mt2a_l1 = metaltop.space(0.38.um, euclidian).polygons(0.001)
+mt2a_l1.output("MT.2a", "MT.2a : min. metaltop spacing : 0.38µm")
+mt2a_l1.forget
+
+# Rule MT.4: Minimum MetalTop area is 0.5625µm²
+logger.info("Executing rule MT.4")
+mt4_l1 = metaltop.with_area(nil, 0.5625.um)
+mt4_l1.output("MT.4", "MT.4 : Minimum MetalTop area : 0.5625µm²")
+mt4_l1.forget
+elsif METAL_TOP == "9K"
+logger.info("MetalTop thickness 9k/11k section")
+
+# Rule MT.1: min. metaltop width is 0.44µm
+logger.info("Executing rule MT.1")
+mt1_l1 = metaltop.width(0.44.um, euclidian).polygons(0.001)
+mt1_l1.output("MT.1", "MT.1 : min. metaltop width : 0.44µm")
+mt1_l1.forget
+
+# Rule MT.2a: min. metaltop spacing is 0.46µm
+logger.info("Executing rule MT.2a")
+mt2a_l1 = metaltop.space(0.46.um, euclidian).polygons(0.001)
+mt2a_l1.output("MT.2a", "MT.2a : min. metaltop spacing : 0.46µm")
+mt2a_l1.forget
+
+# Rule MT.4: Minimum MetalTop area is 0.5625µm²
+logger.info("Executing rule MT.4")
+mt4_l1 = metaltop.with_area(nil, 0.5625.um)
+mt4_l1.output("MT.4", "MT.4 : Minimum MetalTop area : 0.5625µm²")
+mt4_l1.forget
+elsif METAL_TOP == "30K"
+logger.info("MetalTop thickness 30K section")
+
+# Rule MT30.1a: Min. thick MetalTop width. is 1.8µm
+logger.info("Executing rule MT30.1a")
+mt301a_l1 = metaltop.width(1.8.um, euclidian).polygons(0.001)
+mt301a_l1.output("MT30.1a", "MT30.1a : Min. thick MetalTop width. : 1.8µm")
+mt301a_l1.forget
+
+# Rule MT30.1b: Min width for >1000um long metal line (based on metal edge). is 2.2µm
+logger.info("Executing rule MT30.1b")
+mt301b_l1 = metaltop.interacting(metaltop.edges.with_length(1000.um, nil)).width(2.2.um, euclidian).polygons(0.001)
+mt301b_l1.output("MT30.1b", "MT30.1b : Min width for >1000um long metal line (based on metal edge). : 2.2µm")
+mt301b_l1.forget
+
+# Rule MT30.2: Min. thick MetalTop space. is 1.8µm
+logger.info("Executing rule MT30.2")
+mt302_l1 = metaltop.space(1.8.um, euclidian).polygons(0.001)
+mt302_l1.output("MT30.2", "MT30.2 : Min. thick MetalTop space. : 1.8µm")
+mt302_l1.forget
+
+# Rule MT30.3: The separation of two corners should satisfy the minimum spacing. is 1.8µm
+logger.info("Executing rule MT30.3")
+mt303_l1 = metaltop.space(1.8.um, euclidian).polygons(0.001)
+mt303_l1.output("MT30.3", "MT30.3 : The separation of two corners should satisfy the minimum spacing. : 1.8µm")
+mt303_l1.forget
+
+# Rule MT30.4: The separation of single metal line from a any degree metal line should satisfy the minimum spacing. is 1.8µm
+logger.info("Executing rule MT30.4")
+mt304_l1 = metaltop.space(1.8.um, euclidian).polygons(0.001)
+mt304_l1.output("MT30.4", "MT30.4 : The separation of single metal line from a any degree metal line should satisfy the minimum spacing. : 1.8µm")
+mt304_l1.forget
+
+# Rule MT30.5: Minimum thick MetalTop enclose underlying via (for example: via5 for 6LM case) [Outside Not Allowed].
+logger.info("Executing rule MT30.5")
+mt305_l1 = top_metal.enclosing(top_via, 0.12.um, euclidian).polygons(0.001).or(top_via.not_inside(top_metal))
+mt305_l1.output("MT30.5", "MT30.5 : Minimum thick MetalTop enclose underlying via (for example: via5 for 6LM case) [Outside Not Allowed].")
+mt305_l1.forget
+
+mt30p6_cond = top_metal.drc( width <= 0.34.um)
+mt30p6_eol = top_metal.edges.with_length(nil, 0.34.um).interacting(mt30p6_cond.first_edges).interacting(mt30p6_cond.second_edges).not(mt30p6_cond.first_edges).not(mt30p6_cond.second_edges)
+# Rule MT30.6: Thick MetalTop end-of-line (width <2.5um) enclose underlying via (for example: via5 for 6LM case) [Outside Not Allowed].
+logger.info("Executing rule MT30.6")
+mt306_l1 = mt30p6_eol.enclosing(top_via.edges,0.25.um, projection).polygons(0.001).or(top_via.not_inside(top_metal))
+mt306_l1.output("MT30.6", "MT30.6 : Thick MetalTop end-of-line (width <2.5um) enclose underlying via (for example: via5 for 6LM case) [Outside Not Allowed].")
+mt306_l1.forget
+
+mt30p6_cond.forget
+mt30p6_eol.forget
+mt30p8_via_no_mim = top_via.sized(0.18.um).sized(-0.18.um).with_bbox_min(0.78.um , nil).extents.inside(top_metal)
+mt30p8_via_mim = top_via.interacting(fusetop).sized(0.3.um).sized(-0.3.um).with_bbox_min(1.02.um , nil).extents.inside(top_metal)
+mt30p8_via = mt30p8_via_no_mim.or(mt30p8_via_mim)
+mt30p8_mask = mt30p8_via.size(1).not(top_via).with_holes(4, nil)
+mt30p8_slct_via = top_via.interacting(mt30p8_mask)
+# Rule MT30.8: There shall be minimum 2X2 array of vias (top vias) at one location connecting to 3um thick top metal.
+logger.info("Executing rule MT30.8")
+mt308_l1 = topmin1_metal.outside(guard_ring_mk).not_interacting(mt30p8_slct_via)
+mt308_l1.output("MT30.8", "MT30.8 : There shall be minimum 2X2 array of vias (top vias) at one location connecting to 3um thick top metal.")
+mt308_l1.forget
+
+mt30p8_via.forget
+mt30p8_mask.forget
+mt30p8_slct_via.forget
+end #METAL_TOP
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/mim_capacitor_option_a_.drc b/rules/klayout/drc/rule_decks/mim_capacitor_option_a_.drc
new file mode 100644
index 0000000..1797bcd
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/mim_capacitor_option_a_.drc
@@ -0,0 +1,327 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#--------------------------------------------------- GF 0.18um MCU DRC RULE DECK (MIM CAPACITOR OPTION A ) ---------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+via1 = polygons(35 , 0 )
+metal2 = polygons(36 , 0 )
+via2 = polygons(38 , 0 )
+fusetop = polygons(75 , 0 )
+cap_mk = polygons(117, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#------------MIM CAPACITOR OPTION A -------------
+#================================================
+
+if MIM_OPTION == "A"
+logger.info("MIM Capacitor Option A section")
+
+mim_virtual = fusetop.sized(1.06.um).and(metal2.interacting(fusetop))
+# Rule MIM.1: Minimum MiM bottom plate spacing to the bottom plate metal (whether adjacent MiM or routing metal). is 1.2µm
+logger.info("Executing rule MIM.1")
+mim1_l1 = metal2.separation(mim_virtual ,transparent, 1.2.um).polygons(0.001)
+mim1_l1.output("MIM.1", "MIM.1 : Minimum MiM bottom plate spacing to the bottom plate metal (whether adjacent MiM or routing metal). : 1.2µm")
+mim1_l1.forget
+
+# Rule MIM.2: Minimum MiM bottom plate overlap of Via2 layer. [This is applicable for via2 within 1.06um oversize of FuseTop layer (referenced to virtual bottom plate)]. is 0.4µm
+logger.info("Executing rule MIM.2")
+mim2_l1 = metal2.enclosing(via2.overlapping(mim_virtual), 0.4.um, euclidian).polygons(0.001)
+mim2_l2 = via2.overlapping(mim_virtual).not_outside(metal2).not(metal2)
+mim2_l = mim2_l1.or(mim2_l2)
+mim2_l.output("MIM.2", "MIM.2 : Minimum MiM bottom plate overlap of Via2 layer. [This is applicable for via2 within 1.06um oversize of FuseTop layer (referenced to virtual bottom plate)]. : 0.4µm")
+mim2_l1.forget
+mim2_l2.forget
+mim2_l.forget
+
+# Rule MIM.3: Minimum MiM bottom plate overlap of Top plate.
+logger.info("Executing rule MIM.3")
+mim3_l1 = mim_virtual.enclosing(fusetop,0.6.um).polygons(0.001).or(fusetop.not_inside(mim_virtual))
+mim3_l1.output("MIM.3", "MIM.3 : Minimum MiM bottom plate overlap of Top plate.")
+mim3_l1.forget
+
+mim_virtual.forget
+# Rule MIM.4: Minimum MiM top plate (FuseTop) overlap of Via2. is 0.4µm
+logger.info("Executing rule MIM.4")
+mim4_l1 = fusetop.enclosing(via2, 0.4.um, euclidian).polygons(0.001)
+mim4_l2 = via2.not_outside(fusetop).not(fusetop)
+mim4_l = mim4_l1.or(mim4_l2)
+mim4_l.output("MIM.4", "MIM.4 : Minimum MiM top plate (FuseTop) overlap of Via2. : 0.4µm")
+mim4_l1.forget
+mim4_l2.forget
+mim4_l.forget
+
+# Rule MIM.5: Minimum spacing between top plate and the Via2 connecting to the bottom plate. is 0.4µm
+logger.info("Executing rule MIM.5")
+mim5_l1 = fusetop.separation(via2.interacting(metal2), 0.4.um, euclidian).polygons(0.001)
+mim5_l1.output("MIM.5", "MIM.5 : Minimum spacing between top plate and the Via2 connecting to the bottom plate. : 0.4µm")
+mim5_l1.forget
+
+# Rule MIM.6: Minimum spacing between unrelated top plates. is 0.6µm
+logger.info("Executing rule MIM.6")
+mim6_l1 = fusetop.space(0.6.um, euclidian).polygons(0.001)
+mim6_l1.output("MIM.6", "MIM.6 : Minimum spacing between unrelated top plates. : 0.6µm")
+mim6_l1.forget
+
+# Rule MIM.7: Min FuseTop enclosure by CAP_MK.
+logger.info("Executing rule MIM.7")
+mim7_l1 = fusetop.not_inside(cap_mk)
+mim7_l1.output("MIM.7", "MIM.7 : Min FuseTop enclosure by CAP_MK.")
+mim7_l1.forget
+
+# Rule MIM.8a: Minimum MIM cap area (defined by FuseTop area) (um2). is 25µm²
+logger.info("Executing rule MIM.8a")
+mim8a_l1 = fusetop.with_area(nil, 25.um)
+mim8a_l1.output("MIM.8a", "MIM.8a : Minimum MIM cap area (defined by FuseTop area) (um2). : 25µm²")
+mim8a_l1.forget
+# Rule MIM.8b: Maximum single MIM Cap area (Use multiple MIM caps in parallel connection if bigger capacitors are required) (um2). is 10000µm
+logger.info("Executing rule MIM.8b")
+mim8b_l1 = fusetop.with_area(10000.um,nil).not_in(fusetop.with_area(10000.um))
+mim8b_l1.output("MIM.8b", "MIM.8b : Maximum single MIM Cap area (Use multiple MIM caps in parallel connection if bigger capacitors are required) (um2). : 10000µm")
+mim8b_l1.forget
+
+# Rule MIM.9: Min. via spacing for sea of via on MIM top plate. is 0.5µm
+logger.info("Executing rule MIM.9")
+mim9_l1 = via2.inside(fusetop).space(0.5.um, euclidian).polygons(0.001)
+mim9_l1.output("MIM.9", "MIM.9 : Min. via spacing for sea of via on MIM top plate. : 0.5µm")
+mim9_l1.forget
+
+# Rule MIM.10: (a) There cannot be any Via1 touching MIM bottom plate Metal2. (b) MIM bottom plate Metal2 can only be connected through the higher Via (Via2).
+logger.info("Executing rule MIM.10")
+mim10_l1 = via1.interacting(metal2.interacting(fusetop))
+mim10_l1.output("MIM.10", "MIM.10 : (a) There cannot be any Via1 touching MIM bottom plate Metal2. (b) MIM bottom plate Metal2 can only be connected through the higher Via (Via2).")
+mim10_l1.forget
+
+mim11_large_metal2 = metal2.interacting(fusetop).with_area(10000, nil)
+mim11_large_metal2_violation = polygon_layer
+mim11_large_metal2.data.each do |p|
+ mim11_metal2_polygon_layer = polygon_layer
+ mim11_metal2_polygon_layer.data.insert(p)
+ fuse_in_polygon = fusetop.and(mim11_metal2_polygon_layer)
+ if(fuse_in_polygon.area > 10000)
+ mim11_bad_metal2_polygon = mim11_metal2_polygon_layer.interacting(fuse_in_polygon)
+ mim11_bad_metal2_polygon.data.each do |b|
+ b.num_points > 0 && mim11_large_metal2_violation.data.insert(b)
+ end
+ end
+end
+# Rule MIM.11: Bottom plate of multiple MIM caps can be shared (for common nodes) as long as total MIM area with that single common plate does not exceed MIM.8b rule. is -µm
+logger.info("Executing rule MIM.11")
+mim11_l1 = mim11_large_metal2_violation
+mim11_l1.output("MIM.11", "MIM.11 : Bottom plate of multiple MIM caps can be shared (for common nodes) as long as total MIM area with that single common plate does not exceed MIM.8b rule. : -µm")
+mim11_l1.forget
+
+mim11_large_metal2.forget
+mim11_large_metal2_violation.forget
+# rule MIM.12 is not a DRC check
+
+
+else
+logger.info("MIM Capacitor Option A not Selected")
+
+end #MIM_OPTION
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/mim_capacitor_option_b.drc b/rules/klayout/drc/rule_decks/mim_capacitor_option_b.drc
new file mode 100644
index 0000000..47c12b3
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/mim_capacitor_option_b.drc
@@ -0,0 +1,375 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#--------------------------------------------------- GF 0.18um MCU DRC RULE DECK (MIM CAPACITOR OPTION B) ----------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+fusetop = polygons(75 , 0 )
+cap_mk = polygons(117, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#------------- METAL LEVEL SWITCHES -------------
+#================================================
+
+
+if METAL_LEVEL == "6LM"
+ via4 = polygons(41 , 0 )
+ metal5 = polygons(81 , 0 )
+ via5 = polygons(82 , 0 )
+ metaltop = polygons(53 , 0 )
+ top_via = via5
+ topmin1_via = via4
+ top_metal = metaltop
+ topmin1_metal = metal5
+elsif METAL_LEVEL == "5LM"
+ via3 = polygons(40 , 0 )
+ metal4 = polygons(46 , 0 )
+ via4 = polygons(41 , 0 )
+ metal5 = polygons(81 , 0 )
+ top_via = via4
+ topmin1_via = via3
+ top_metal = metal5
+ topmin1_metal = metal4
+elsif METAL_LEVEL == "4LM"
+ via2 = polygons(38 , 0 )
+ metal3 = polygons(42 , 0 )
+ via3 = polygons(40 , 0 )
+ metal4 = polygons(46 , 0 )
+ top_via = via3
+ topmin1_via = via2
+ top_metal = metal4
+ topmin1_metal = metal3
+elsif METAL_LEVEL == "3LM"
+ via1 = polygons(35 , 0 )
+ metal2 = polygons(36 , 0 )
+ via2 = polygons(38 , 0 )
+ metal3 = polygons(42 , 0 )
+ top_via = via2
+ topmin1_via = via1
+ top_metal = metal3
+ topmin1_metal = metal2
+elsif METAL_LEVEL == "2LM"
+ metal1 = polygons(34 , 0 )
+ via1 = polygons(35 , 0 )
+ metal2 = polygons(36 , 0 )
+ top_via = via1
+ topmin1_via = via1
+ top_metal = metal2
+ topmin1_metal = metal1
+end #METAL_LEVEL
+
+
+#================================================
+#-------------MIM CAPACITOR OPTION B-------------
+#================================================
+
+if MIM_OPTION == "B"
+logger.info("mim11_metal2_polygon_layer.interacting(fuse_in_polygon) section")
+
+mimtm_virtual = fusetop.sized(1.06.um).and(topmin1_metal.interacting(fusetop))
+# Rule MIMTM.1: Minimum MiM bottom plate spacing to the bottom plate metal (whether adjacent MiM or routing metal). is 1.2µm
+logger.info("Executing rule MIMTM.1")
+mimtm1_l1 = topmin1_metal.separation(mimtm_virtual ,transparent, 1.2.um).polygons(0.001)
+mimtm1_l1.output("MIMTM.1", "MIMTM.1 : Minimum MiM bottom plate spacing to the bottom plate metal (whether adjacent MiM or routing metal). : 1.2µm")
+mimtm1_l1.forget
+
+# Rule MIMTM.2: Minimum MiM bottom plate overlap of Vian-1 layer. [This is applicable for Vian-1 within 1.06um oversize of FuseTop layer (referenced to virtual bottom plate)]. is 0.4µm
+logger.info("Executing rule MIMTM.2")
+mimtm2_l1 = topmin1_metal.enclosing(top_via.overlapping(mimtm_virtual), 0.4.um, euclidian).polygons(0.001)
+mimtm2_l2 = top_via.overlapping(mimtm_virtual).not_outside(topmin1_metal).not(topmin1_metal)
+mimtm2_l = mimtm2_l1.or(mimtm2_l2)
+mimtm2_l.output("MIMTM.2", "MIMTM.2 : Minimum MiM bottom plate overlap of Vian-1 layer. [This is applicable for Vian-1 within 1.06um oversize of FuseTop layer (referenced to virtual bottom plate)]. : 0.4µm")
+mimtm2_l1.forget
+mimtm2_l2.forget
+mimtm2_l.forget
+
+# Rule MIMTM.3: Minimum MiM bottom plate overlap of Top plate.
+logger.info("Executing rule MIMTM.3")
+mimtm3_l1 = mimtm_virtual.enclosing(fusetop,0.6.um).polygons(0.001).or(fusetop.not_inside(mimtm_virtual))
+mimtm3_l1.output("MIMTM.3", "MIMTM.3 : Minimum MiM bottom plate overlap of Top plate.")
+mimtm3_l1.forget
+
+mimtm_virtual.forget
+# Rule MIMTM.4: Minimum MiM top plate (FuseTop) overlap of Vian-1. is 0.4µm
+logger.info("Executing rule MIMTM.4")
+mimtm4_l1 = fusetop.enclosing(top_via, 0.4.um, euclidian).polygons(0.001)
+mimtm4_l2 = top_via.not_outside(fusetop).not(fusetop)
+mimtm4_l = mimtm4_l1.or(mimtm4_l2)
+mimtm4_l.output("MIMTM.4", "MIMTM.4 : Minimum MiM top plate (FuseTop) overlap of Vian-1. : 0.4µm")
+mimtm4_l1.forget
+mimtm4_l2.forget
+mimtm4_l.forget
+
+# Rule MIMTM.5: Minimum spacing between top plate and the Vian-1 connecting to the bottom plate. is 0.4µm
+logger.info("Executing rule MIMTM.5")
+mimtm5_l1 = fusetop.separation(top_via.interacting(topmin1_metal), 0.4.um, euclidian).polygons(0.001)
+mimtm5_l1.output("MIMTM.5", "MIMTM.5 : Minimum spacing between top plate and the Vian-1 connecting to the bottom plate. : 0.4µm")
+mimtm5_l1.forget
+
+# Rule MIMTM.6: Minimum spacing between unrelated top plates. is 0.6µm
+logger.info("Executing rule MIMTM.6")
+mimtm6_l1 = fusetop.space(0.6.um, euclidian).polygons(0.001)
+mimtm6_l1.output("MIMTM.6", "MIMTM.6 : Minimum spacing between unrelated top plates. : 0.6µm")
+mimtm6_l1.forget
+
+# Rule MIMTM.7: Min FuseTop enclosure by CAP_MK.
+logger.info("Executing rule MIMTM.7")
+mimtm7_l1 = fusetop.not_inside(cap_mk)
+mimtm7_l1.output("MIMTM.7", "MIMTM.7 : Min FuseTop enclosure by CAP_MK.")
+mimtm7_l1.forget
+
+# Rule MIMTM.8a: Minimum MIM cap area (defined by FuseTop area) (um2). is 25µm²
+logger.info("Executing rule MIMTM.8a")
+mimtm8a_l1 = fusetop.with_area(nil, 25.um)
+mimtm8a_l1.output("MIMTM.8a", "MIMTM.8a : Minimum MIM cap area (defined by FuseTop area) (um2). : 25µm²")
+mimtm8a_l1.forget
+# Rule MIMTM.8b: Maximum single MIM Cap area (Use multiple MIM caps in parallel connection if bigger capacitors are required) (um2). is 10000µm
+logger.info("Executing rule MIMTM.8b")
+mimtm8b_l1 = fusetop.with_area(10000.um,nil).not_in(fusetop.with_area(10000.um))
+mimtm8b_l1.output("MIMTM.8b", "MIMTM.8b : Maximum single MIM Cap area (Use multiple MIM caps in parallel connection if bigger capacitors are required) (um2). : 10000µm")
+mimtm8b_l1.forget
+
+# Rule MIMTM.9: Min. Via (Vian-1) spacing for sea of Via on MIM top plate. is 0.5µm
+logger.info("Executing rule MIMTM.9")
+mimtm9_l1 = top_via.inside(fusetop).space(0.5.um, euclidian).polygons(0.001)
+mimtm9_l1.output("MIMTM.9", "MIMTM.9 : Min. Via (Vian-1) spacing for sea of Via on MIM top plate. : 0.5µm")
+mimtm9_l1.forget
+
+# Rule MIMTM.10: (a) There cannot be any Vian-2 touching MIM bottom plate Metaln-1. (b) MIM bottom plate Metaln-1 can only be connected through the higher Via (Vian-1).
+logger.info("Executing rule MIMTM.10")
+mimtm10_l1 = topmin1_via.interacting(topmin1_metal.interacting(fusetop))
+mimtm10_l1.output("MIMTM.10", "MIMTM.10 : (a) There cannot be any Vian-2 touching MIM bottom plate Metaln-1. (b) MIM bottom plate Metaln-1 can only be connected through the higher Via (Vian-1).")
+mimtm10_l1.forget
+
+mimtm11_large_topmin1_metal = topmin1_metal.interacting(fusetop).with_area(10000, nil)
+mimtm11_large_topmin1_metal_violation = polygon_layer
+mimtm11_large_topmin1_metal.data.each do |p|
+ mimtm11_topmin1_metal_polygon_layer = polygon_layer
+ mimtm11_topmin1_metal_polygon_layer.data.insert(p)
+ fuse_in_polygon = fusetop.and(mimtm11_topmin1_metal_polygon_layer)
+ if(fuse_in_polygon.area > 10000)
+ mimtm11_bad_topmin1_metal_polygon = mimtm11_topmin1_metal_polygon_layer.interacting(fuse_in_polygon)
+ mimtm11_bad_topmin1_metal_polygon.data.each do |b|
+ b.num_points > 0 && mimtm11_large_topmin1_metal_violation.data.insert(b)
+ end
+ end
+end
+# Rule MIMTM.11: Bottom plate of multiple MIM caps can be shared (for common nodes) as long as total MIM area with that single common plate does not exceed MIMTM.8b rule. is -µm
+logger.info("Executing rule MIMTM.11")
+mimtm11_l1 = mimtm11_large_topmin1_metal_violation
+mimtm11_l1.output("MIMTM.11", "MIMTM.11 : Bottom plate of multiple MIM caps can be shared (for common nodes) as long as total MIM area with that single common plate does not exceed MIMTM.8b rule. : -µm")
+mimtm11_l1.forget
+
+mimtm11_large_topmin1_metal.forget
+mimtm11_large_topmin1_metal_violation.forget
+# rule MIMTM.12 is not a DRC check
+
+else
+logger.info("MIM Capacitor Option B not Selected")
+
+end #MIM_OPTION
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/n+_poly_resistor.drc b/rules/klayout/drc/rule_decks/n+_poly_resistor.drc
new file mode 100644
index 0000000..2cdb99f
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/n+_poly_resistor.drc
@@ -0,0 +1,287 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (N+ POLY RESISTOR) -------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+resistor = polygons(62 , 0 )
+res_mk = polygons(110, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------N+ POLY RESISTOR----------------
+#================================================
+
+lres_poly = poly2.and(nplus).interacting(sab).interacting(res_mk)
+# Rule LRES.1: Minimum width of Poly2 resistor. is 0.8µm
+logger.info("Executing rule LRES.1")
+lres1_l1 = lres_poly.width(0.8.um, euclidian).polygons(0.001)
+lres1_l1.output("LRES.1", "LRES.1 : Minimum width of Poly2 resistor. : 0.8µm")
+lres1_l1.forget
+
+# Rule LRES.2: Minimum space between Poly2 resistors. is 0.4µm
+logger.info("Executing rule LRES.2")
+lres2_l1 = lres_poly.isolated(0.4.um, euclidian).polygons(0.001)
+lres2_l1.output("LRES.2", "LRES.2 : Minimum space between Poly2 resistors. : 0.4µm")
+lres2_l1.forget
+
+# Rule LRES.3: Minimum space from Poly2 resistor to COMP.
+logger.info("Executing rule LRES.3")
+lres3_l1 = lres_poly.separation(comp, 0.6.um, euclidian).polygons(0.001).or(comp.not_outside(lres_poly))
+lres3_l1.output("LRES.3", "LRES.3 : Minimum space from Poly2 resistor to COMP.")
+lres3_l1.forget
+
+# Rule LRES.4: Minimum space from Poly2 resistor to unrelated Poly2. is 0.6µm
+logger.info("Executing rule LRES.4")
+lres4_l1 = lres_poly.separation(poly2.not_interacting(sab), 0.6.um, euclidian).polygons(0.001)
+lres4_l1.output("LRES.4", "LRES.4 : Minimum space from Poly2 resistor to unrelated Poly2. : 0.6µm")
+lres4_l1.forget
+
+# Rule LRES.5: Minimum Nplus implant overlap of Poly2 resistor. is 0.3µm
+logger.info("Executing rule LRES.5")
+lres5_l1 = nplus.enclosing(poly2.and(nplus).interacting(sab).interacting(res_mk), 0.3.um, euclidian).polygons(0.001)
+lres5_l2 = poly2.and(nplus).interacting(sab).interacting(res_mk).not_outside(nplus).not(nplus)
+lres5_l = lres5_l1.or(lres5_l2)
+lres5_l.output("LRES.5", "LRES.5 : Minimum Nplus implant overlap of Poly2 resistor. : 0.3µm")
+lres5_l1.forget
+lres5_l2.forget
+lres5_l.forget
+
+# Rule LRES.6: Minimum salicide block overlap of Poly2 resistor in width direction. is 0.28µm
+logger.info("Executing rule LRES.6")
+lres6_l1 = sab.enclosing(lres_poly,0.28.um).polygons(0.001)
+lres6_l1.output("LRES.6", "LRES.6 : Minimum salicide block overlap of Poly2 resistor in width direction. : 0.28µm")
+lres6_l1.forget
+
+cont_lres7 = contact.inside(poly2.and(nplus).interacting(sab).interacting(res_mk))
+# Rule LRES.7: Space from salicide block to contact on Poly2 resistor.
+logger.info("Executing rule LRES.7")
+lres7_l1 = cont_lres7.separation(sab,0.22.um).polygons(0.001).or(cont_lres7.interacting(sab))
+lres7_l1.output("LRES.7", "LRES.7 : Space from salicide block to contact on Poly2 resistor.")
+lres7_l1.forget
+
+cont_lres7.forget
+# rule LRES.8 is not a DRC check
+
+mk_lres9 = res_mk.edges.not(poly2.and(nplus).and(sab).edges).inside_part(poly2)
+# Rule LRES.9a: Nplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.
+logger.info("Executing rule LRES.9a")
+lres9a_l1 = res_mk.interacting(lres_poly).interacting(mk_lres9)
+lres9a_l1.output("LRES.9a", "LRES.9a : Nplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2. ")
+lres9a_l1.forget
+
+mk_lres9.forget
+lres9b = res_mk.with_area(15000.01.um,nil).in(res_mk.interacting(res_mk.edges.with_length(80.01.um,nil)))
+# Rule LRES.9b: If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. is 20µm
+logger.info("Executing rule LRES.9b")
+lres9b_l1 = res_mk.interacting(lres_poly).drc(separation(lres9b) < 20.um).polygons(0.001)
+lres9b_l1.output("LRES.9b", "LRES.9b : If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. : 20µm")
+lres9b_l1.forget
+
+lres9b.forget
+lres_poly.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/native_vt_nmos.drc b/rules/klayout/drc/rule_decks/native_vt_nmos.drc
new file mode 100644
index 0000000..c8215fd
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/native_vt_nmos.drc
@@ -0,0 +1,309 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (NATIVE VT NMOS) --------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+nwell = polygons(21 , 0 )
+nat = polygons(5 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+natcompsd = (nat & comp.interacting(poly2)) - tgate
+
+if CONNECTIVITY_RULES
+ logger.info("Construct connectivity for NAT layer.")
+ connect(natcompsd, contact)
+end #CONNECTIVITY_RULES
+
+
+
+#================================================
+#-----------------NATIVE VT NMOS-----------------
+#================================================
+
+# Rule NAT.1: Min. NAT Overlap of COMP of Native Vt NMOS. is 2µm
+logger.info("Executing rule NAT.1")
+nat1_l1 = nat.enclosing(ncomp.outside(nwell).interacting(nat), 2.um, euclidian).polygons(0.001)
+nat1_l1.output("NAT.1", "NAT.1 : Min. NAT Overlap of COMP of Native Vt NMOS. : 2µm")
+nat1_l1.forget
+
+# Rule NAT.2: Space to unrelated COMP (outside NAT). is 0.3µm
+logger.info("Executing rule NAT.2")
+nat2_l1 = nat.separation(comp.outside(nat), 0.3.um, euclidian).polygons(0.001)
+nat2_l1.output("NAT.2", "NAT.2 : Space to unrelated COMP (outside NAT). : 0.3µm")
+nat2_l1.forget
+
+# Rule NAT.3: Space to NWell edge. is 0.5µm
+logger.info("Executing rule NAT.3")
+nat3_l1 = nat.separation(nwell, 0.5.um, euclidian).polygons(0.001)
+nat3_l1.output("NAT.3", "NAT.3 : Space to NWell edge. : 0.5µm")
+nat3_l1.forget
+
+# Rule NAT.4: Minimum channel length for 3.3V Native Vt NMOS (For smaller L Ioff will be higher than Spec). is 1.8µm
+logger.info("Executing rule NAT.4")
+nat4_l1 = poly2.edges.and(ngate.edges).not(nwell).interacting(nat).width(1.8.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+nat4_l1.output("NAT.4", "NAT.4 : Minimum channel length for 3.3V Native Vt NMOS (For smaller L Ioff will be higher than Spec). : 1.8µm")
+nat4_l1.forget
+
+# Rule NAT.5: Minimum channel length for 6.0V Native Vt NMOS (For smaller L Ioff will be higher than Spec). is 1.8µm
+logger.info("Executing rule NAT.5")
+nat5_l1 = poly2.edges.and(ngate.edges).not(nwell).interacting(nat).width(1.8.um, euclidian).polygons(0.001).overlapping(dualgate)
+nat5_l1.output("NAT.5", "NAT.5 : Minimum channel length for 6.0V Native Vt NMOS (For smaller L Ioff will be higher than Spec). : 1.8µm")
+nat5_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_nat, unconnected_nat = conn_space(natcompsd, 10, 10, transparent)
+
+# Rule NAT.6: Two or more COMPs if connected to different potential are not allowed under same NAT layer.
+logger.info("Executing rule NAT.6")
+nat6_l1 = comp.and(nat).interacting(unconnected_nat.inside(nat.covering(comp, 2)).not(poly2))
+nat6_l1.output("NAT.6", "NAT.6 : Two or more COMPs if connected to different potential are not allowed under same NAT layer.")
+nat6_l1.forget
+
+end #CONNECTIVITY_RULES
+
+natcompsd.forget
+# Rule NAT.7: Minimum NAT to NAT spacing. is 0.74µm
+logger.info("Executing rule NAT.7")
+nat7_l1 = nat.space(0.74.um, euclidian).polygons(0.001)
+nat7_l1.output("NAT.7", "NAT.7 : Minimum NAT to NAT spacing. : 0.74µm")
+nat7_l1.forget
+
+# Rule NAT.8: Min. Dualgate overlap of NAT (for 5V/6V) native VT NMOS only.
+logger.info("Executing rule NAT.8")
+nat8_l1 = nat.not_outside(dualgate).not(dualgate)
+nat8_l1.output("NAT.8", "NAT.8 : Min. Dualgate overlap of NAT (for 5V/6V) native VT NMOS only.")
+nat8_l1.forget
+
+nat9_1 = poly2.and(nat).not(ncomp).interacting(ngate.and(nat) , 2)
+nat9_2 = poly2.not(nat).separation(nat, 0.3.um, euclidian).polygons(0.001)
+# Rule NAT.9: Poly interconnect under NAT layer is not allowed, minimum spacing of un-related poly from the NAT layer.
+logger.info("Executing rule NAT.9")
+nat9_l1 = nat9_1.or(nat9_2)
+nat9_l1.output("NAT.9", "NAT.9 : Poly interconnect under NAT layer is not allowed, minimum spacing of un-related poly from the NAT layer.")
+nat9_l1.forget
+
+nat9_1.forget
+nat9_2.forget
+# Rule NAT.10: Nwell, inside NAT layer are not allowed.
+logger.info("Executing rule NAT.10")
+nat10_l1 = nwell.inside(nat)
+nat10_l1.output("NAT.10", "NAT.10 : Nwell, inside NAT layer are not allowed.")
+nat10_l1.forget
+
+# Rule NAT.11: NCOMP not intersecting to Poly2, is not allowed inside NAT layer.
+logger.info("Executing rule NAT.11")
+nat11_l1 = ncomp.and(nat).outside(poly2)
+nat11_l1.output("NAT.11", "NAT.11 : NCOMP not intersecting to Poly2, is not allowed inside NAT layer.")
+nat11_l1.forget
+
+# Rule NAT.12: Poly2 not intersecting with COMP is not allowed inside NAT (Poly2 resistor is not allowed inside NAT).
+logger.info("Executing rule NAT.12")
+nat12_l1 = poly2.interacting(nat).not_interacting(comp.and(nat))
+nat12_l1.output("NAT.12", "NAT.12 : Poly2 not intersecting with COMP is not allowed inside NAT (Poly2 resistor is not allowed inside NAT).")
+nat12_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/nplus.drc b/rules/klayout/drc/rule_decks/nplus.drc
new file mode 100644
index 0000000..c9e1136
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/nplus.drc
@@ -0,0 +1,420 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (NPLUS) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+sab = polygons(49 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------NPLUS----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule NP.1: min. nplus width is 0.4µm
+logger.info("Executing rule NP.1")
+np1_l1 = nplus.width(0.4.um, euclidian).polygons(0.001)
+np1_l1.output("NP.1", "NP.1 : min. nplus width : 0.4µm")
+np1_l1.forget
+
+# Rule NP.2: min. nplus spacing is 0.4µm
+logger.info("Executing rule NP.2")
+np2_l1 = nplus.space(0.4.um, euclidian).polygons(0.001)
+np2_l1.output("NP.2", "NP.2 : min. nplus spacing : 0.4µm")
+np2_l1.forget
+
+# Rule NP.3a: Space to PCOMP for PCOMP: (1) Inside Nwell (2) Outside LVPWELL but inside DNWELL. is 0.16µm
+logger.info("Executing rule NP.3a")
+np3a_l1 = nplus.separation((pcomp.inside(nwell)).or(pcomp.outside(lvpwell).inside(dnwell)), 0.16.um, euclidian).polygons(0.001)
+np3a_l1.output("NP.3a", "NP.3a : Space to PCOMP for PCOMP: (1) Inside Nwell (2) Outside LVPWELL but inside DNWELL. : 0.16µm")
+np3a_l1.forget
+
+np_3bi_extend = lvpwell.inside(dnwell).sized(-0.429.um)
+np_3bi = pcomp.edges.and(lvpwell.inside(dnwell).not(np_3bi_extend))
+# Rule NP.3bi: Space to PCOMP: For Inside DNWELL, inside LVPWELL:(i) For PCOMP overlap by LVPWELL < 0.43um. is 0.16µm
+logger.info("Executing rule NP.3bi")
+np3bi_l1 = nplus.not_outside(lvpwell).inside(dnwell).edges.separation(np_3bi, 0.16.um, euclidian).polygons(0.001)
+np3bi_l1.output("NP.3bi", "NP.3bi : Space to PCOMP: For Inside DNWELL, inside LVPWELL:(i) For PCOMP overlap by LVPWELL < 0.43um. : 0.16µm")
+np3bi_l1.forget
+
+np_3bi_extend.forget
+np_3bi.forget
+np_3bii_extend = lvpwell.inside(dnwell).sized(-0.429.um)
+np_3bii = pcomp.edges.and(np_3bii_extend)
+# Rule NP.3bii: Space to PCOMP: For Inside DNWELL, inside LVPWELL:(ii) For PCOMP overlap by LVPWELL >= 0.43um. is 0.08µm
+logger.info("Executing rule NP.3bii")
+np3bii_l1 = nplus.not_outside(lvpwell).inside(dnwell).edges.separation(np_3bii, 0.08.um, euclidian).polygons(0.001)
+np3bii_l1.output("NP.3bii", "NP.3bii : Space to PCOMP: For Inside DNWELL, inside LVPWELL:(ii) For PCOMP overlap by LVPWELL >= 0.43um. : 0.08µm")
+np3bii_l1.forget
+
+np_3bii_extend.forget
+np_3bii.forget
+np_3ci = pcomp.edges.and(nwell.outside(dnwell).sized(0.429.um))
+# Rule NP.3ci: Space to PCOMP: For Outside DNWELL:(i) For PCOMP space to Nwell < 0.43um. is 0.16µm
+logger.info("Executing rule NP.3ci")
+np3ci_l1 = nplus.outside(dnwell).edges.separation(np_3ci, 0.16.um, euclidian).polygons
+np3ci_l1.output("NP.3ci", "NP.3ci : Space to PCOMP: For Outside DNWELL:(i) For PCOMP space to Nwell < 0.43um. : 0.16µm")
+np3ci_l1.forget
+
+np_3ci.forget
+np_3cii = pcomp.edges.not(nwell.outside(dnwell).sized(0.429.um))
+# Rule NP.3cii: Space to PCOMP: For Outside DNWELL:(ii) For PCOMP space to Nwell >= 0.43um. is 0.08µm
+logger.info("Executing rule NP.3cii")
+np3cii_l1 = nplus.outside(dnwell).edges.separation(np_3cii, 0.08.um, euclidian).polygons
+np3cii_l1.output("NP.3cii", "NP.3cii : Space to PCOMP: For Outside DNWELL:(ii) For PCOMP space to Nwell >= 0.43um. : 0.08µm")
+np3cii_l1.forget
+
+np_3cii.forget
+# Rule NP.3d: Min/max space to a butted PCOMP.
+logger.info("Executing rule NP.3d")
+np3d_l1 = nplus.not_outside(pcomp)
+np3d_l1.output("NP.3d", "NP.3d : Min/max space to a butted PCOMP.")
+np3d_l1.forget
+
+# Rule NP.3e: Space to related PCOMP edge adjacent to a butting edge.
+logger.info("Executing rule NP.3e")
+np3e_l1 = nplus.not_outside(pcomp)
+np3e_l1.output("NP.3e", "NP.3e : Space to related PCOMP edge adjacent to a butting edge.")
+np3e_l1.forget
+
+# Rule NP.4a: Space to related P-channel gate at a butting edge parallel to gate. is 0.32µm
+logger.info("Executing rule NP.4a")
+np4a_l1 = nplus.edges.and(pcomp.edges).separation(pgate.edges, 0.32.um, projection).polygons(0.001)
+np4a_l1.output("NP.4a", "NP.4a : Space to related P-channel gate at a butting edge parallel to gate. : 0.32µm")
+np4a_l1.forget
+
+np_4b_poly = poly2.edges.interacting(pgate.edges.not(pcomp.edges)).centers(0, 0.99).and(pgate.sized(0.32.um))
+# Rule NP.4b: Within 0.32um of channel, space to P-channel gate extension perpendicular to the direction of Poly2.
+logger.info("Executing rule NP.4b")
+np4b_l1 = nplus.interacting(nplus.edges.separation(np_4b_poly, 0.22.um, projection).polygons(0.001))
+np4b_l1.output("NP.4b", "NP.4b : Within 0.32um of channel, space to P-channel gate extension perpendicular to the direction of Poly2.")
+np4b_l1.forget
+
+np_4b_poly.forget
+# Rule NP.5a: Overlap of N-channel gate. is 0.23µm
+logger.info("Executing rule NP.5a")
+np5a_l1 = nplus.enclosing(ngate, 0.23.um, euclidian).polygons(0.001)
+np5a_l2 = ngate.not_outside(nplus).not(nplus)
+np5a_l = np5a_l1.or(np5a_l2)
+np5a_l.output("NP.5a", "NP.5a : Overlap of N-channel gate. : 0.23µm")
+np5a_l1.forget
+np5a_l2.forget
+np5a_l.forget
+
+# Rule NP.5b: Extension beyond COMP for the COMP (1) inside LVPWELL (2) outside Nwell and DNWELL. is 0.16µm
+logger.info("Executing rule NP.5b")
+np5b_l1 = nplus.not_outside(lvpwell).or(nplus.outside(nwell).outside(dnwell)).edges.not(pplus).enclosing(comp.edges, 0.16.um, euclidian).polygons(0.001)
+np5b_l1.output("NP.5b", "NP.5b : Extension beyond COMP for the COMP (1) inside LVPWELL (2) outside Nwell and DNWELL. : 0.16µm")
+np5b_l1.forget
+
+np_5ci_background = nplus.not_inside(lvpwell).inside(dnwell).edges
+np_5ci_foreground = ncomp.not_inside(lvpwell).inside(dnwell).edges.not(pplus.edges).and(lvpwell.inside(dnwell).sized(0.429.um))
+# Rule NP.5ci: Extension beyond COMP: For Inside DNWELL: (i)For Nplus < 0.43um from LVPWELL edge for Nwell or DNWELL tap inside DNWELL. is 0.16µm
+logger.info("Executing rule NP.5ci")
+np5ci_l1 = np_5ci_background.enclosing(np_5ci_foreground, 0.16.um, projection).polygons(0.001)
+np5ci_l1.output("NP.5ci", "NP.5ci : Extension beyond COMP: For Inside DNWELL: (i)For Nplus < 0.43um from LVPWELL edge for Nwell or DNWELL tap inside DNWELL. : 0.16µm")
+np5ci_l1.forget
+
+np_5ci_background.forget
+np_5ci_foreground.forget
+np_5cii_background = nplus.not_inside(lvpwell).inside(dnwell).edges
+np_5cii_foreground = ncomp.not_inside(lvpwell).inside(dnwell).edges.not(pplus.edges).not(lvpwell.inside(dnwell).sized(0.429.um))
+# Rule NP.5cii: Extension beyond COMP: For Inside DNWELL: (ii) For Nplus >= 0.43um from LVPWELL edge for Nwell or DNWELL tap inside DNWELL. is 0.02µm
+logger.info("Executing rule NP.5cii")
+np5cii_l1 = np_5cii_background.enclosing(np_5cii_foreground, 0.02.um, projection).polygons(0.001)
+np5cii_l1.output("NP.5cii", "NP.5cii : Extension beyond COMP: For Inside DNWELL: (ii) For Nplus >= 0.43um from LVPWELL edge for Nwell or DNWELL tap inside DNWELL. : 0.02µm")
+np5cii_l1.forget
+
+np_5cii_background.forget
+np_5cii_foreground.forget
+np_5di_background = nplus.not_outside(nwell).outside(dnwell).edges
+np_5di_extend = nwell.outside(dnwell).not(nwell.outside(dnwell).sized(-0.429.um))
+np_5di_foreground = ncomp.not_outside(nwell).outside(dnwell).edges.not(pplus.edges).and(np_5di_extend)
+# Rule NP.5di: Extension beyond COMP: For Outside DNWELL, inside Nwell: (i) For Nwell overlap of Nplus < 0.43um. is 0.16µm
+logger.info("Executing rule NP.5di")
+np5di_l1 = np_5di_background.enclosing(np_5di_foreground, 0.16.um, projection).polygons(0.001)
+np5di_l1.output("NP.5di", "NP.5di : Extension beyond COMP: For Outside DNWELL, inside Nwell: (i) For Nwell overlap of Nplus < 0.43um. : 0.16µm")
+np5di_l1.forget
+
+np_5di_background.forget
+np_5di_extend.forget
+np_5di_foreground.forget
+np_5dii_background = nplus.not_outside(nwell).outside(dnwell).edges.not(pplus.edges)
+np_5dii_extend = nwell.outside(dnwell).sized(-0.429.um)
+np_5dii_foreground = ncomp.not_outside(nwell).outside(dnwell).edges.not(pplus.edges).and(np_5dii_extend)
+# Rule NP.5dii: Extension beyond COMP: For Outside DNWELL, inside Nwell: (ii) For Nwell overlap of Nplus >= 0.43um. is 0.02µm
+logger.info("Executing rule NP.5dii")
+np5dii_l1 = np_5dii_background.enclosing(np_5dii_foreground, 0.02.um, euclidian).polygons(0.001)
+np5dii_l1.output("NP.5dii", "NP.5dii : Extension beyond COMP: For Outside DNWELL, inside Nwell: (ii) For Nwell overlap of Nplus >= 0.43um. : 0.02µm")
+np5dii_l1.forget
+
+np_5dii_background.forget
+np_5dii_extend.forget
+np_5dii_foreground.forget
+# Rule NP.6: Overlap with NCOMP butted to PCOMP. is 0.22µm
+logger.info("Executing rule NP.6")
+np6_l1 = comp.interacting(nplus).enclosing(pcomp.interacting(nplus), 0.22.um, projection).polygons
+np6_l1.output("NP.6", "NP.6 : Overlap with NCOMP butted to PCOMP. : 0.22µm")
+np6_l1.forget
+
+# Rule NP.7: Space to unrelated unsalicided Poly2. is 0.18µm
+logger.info("Executing rule NP.7")
+np7_l1 = nplus.separation(poly2.and(sab), 0.18.um, euclidian).polygons(0.001)
+np7_l1.output("NP.7", "NP.7 : Space to unrelated unsalicided Poly2. : 0.18µm")
+np7_l1.forget
+
+# Rule NP.8a: Minimum Nplus area (um2). is 0.35µm²
+logger.info("Executing rule NP.8a")
+np8a_l1 = nplus.with_area(nil, 0.35.um)
+np8a_l1.output("NP.8a", "NP.8a : Minimum Nplus area (um2). : 0.35µm²")
+np8a_l1.forget
+# Rule NP.8b: Minimum area enclosed by Nplus (um2). is 0.35µm²
+logger.info("Executing rule NP.8b")
+np8b_l1 = nplus.holes.with_area(nil, 0.35.um)
+np8b_l1.output("NP.8b", "NP.8b : Minimum area enclosed by Nplus (um2). : 0.35µm²")
+np8b_l1.forget
+# Rule NP.9: Overlap of unsalicided Poly2. is 0.18µm
+logger.info("Executing rule NP.9")
+np9_l1 = nplus.enclosing(poly2.and(sab), 0.18.um, euclidian).polygons(0.001)
+np9_l2 = poly2.and(sab).not_outside(nplus).not(nplus)
+np9_l = np9_l1.or(np9_l2)
+np9_l.output("NP.9", "NP.9 : Overlap of unsalicided Poly2. : 0.18µm")
+np9_l1.forget
+np9_l2.forget
+np9_l.forget
+
+# Rule NP.10: Overlap of unsalicided COMP. is 0.18µm
+logger.info("Executing rule NP.10")
+np10_l1 = nplus.enclosing(comp.and(sab), 0.18.um, euclidian).polygons(0.001)
+np10_l1.output("NP.10", "NP.10 : Overlap of unsalicided COMP. : 0.18µm")
+np10_l1.forget
+
+np_11_in_dnwell = nplus.interacting(nplus.edges.and(pcomp.edges).and(lvpwell.inside(dnwell).not(lvpwell.inside(dnwell).sized(-0.429.um))))
+np_11_out_dnwell = nplus.interacting(nplus.edges.and(pcomp.edges).and(nwell.outside(dnwell).sized(0.429.um)))
+# Rule NP.11: Butting Nplus and PCOMP is forbidden within 0.43um of Nwell edge (for outside DNWELL) and of LVPWELL edge (for inside DNWELL case).
+logger.info("Executing rule NP.11")
+np11_l1 = np_11_in_dnwell.or(np_11_out_dnwell)
+np11_l1.output("NP.11", "NP.11 : Butting Nplus and PCOMP is forbidden within 0.43um of Nwell edge (for outside DNWELL) and of LVPWELL edge (for inside DNWELL case).")
+np11_l1.forget
+
+np_11_in_dnwell.forget
+np_11_out_dnwell.forget
+# Rule NP.12: Overlap with P-channel poly2 gate extension is forbidden within 0.32um of P-channel gate.
+logger.info("Executing rule NP.12")
+np12_l1 = nplus.interacting(nplus.edges.separation(pgate.edges.and(pcomp.edges), 0.32.um, euclidian).polygons(0.001))
+np12_l1.output("NP.12", "NP.12 : Overlap with P-channel poly2 gate extension is forbidden within 0.32um of P-channel gate.")
+np12_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/nwell.drc b/rules/klayout/drc/rule_decks/nwell.drc
new file mode 100644
index 0000000..7acdba1
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/nwell.drc
@@ -0,0 +1,448 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (NWELL) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+sab = polygons(49 , 0 )
+res_mk = polygons(110, 5 )
+ymtp_mk = polygons(86 , 17)
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+#================================================
+#------------- LAYERS CONNECTIONS ---------------
+#================================================
+
+if CONNECTIVITY_RULES
+
+ logger.info("Construct connectivity for the design.")
+
+ connect(dnwell, ncomp)
+ connect(ncomp, contact)
+ connect(pcomp, contact)
+ connect(lvpwell, ncomp)
+ connect(nwell, ncomp)
+ connect(natcompsd, contact)
+ connect(mvsd, ncomp)
+ connect(mvpsd, pcomp)
+ connect(contact, metal1)
+ connect(metal1, via1)
+ connect(via1, metal2)
+ connect(metal2, via2)
+ connect(via2, metal3)
+ connect(metal3, via3)
+ connect(via3, metal4)
+ connect(metal4, via4)
+ connect(via4, metal5)
+ connect(metal5, via5)
+ connect(via5, metaltop)
+
+end #CONNECTIVITY_RULES
+
+#================================================
+#------------ PRE-DEFINED FUNCTIONS -------------
+#================================================
+
+def conn_space(layer,conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer.space(conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
+ singularity_errors = layer.space(0.001.um)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
+ return connected_output, unconnected_output
+end
+
+def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
+ if conn_val > not_conn_val
+ raise "ERROR : Wrong connectivity implementation"
+ end
+ connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
+ unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
+ # Filter out the errors arising from the same net
+ unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
+ unconnected_errors_unfiltered.data.each do |ep|
+ net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
+ net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
+ if !net1 || !net2
+ puts "Should not happen ..."
+ elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
+ # unconnected
+ unconnected_errors.data.insert(ep)
+ end
+ end
+ unconnected_output = unconnected_errors.polygons(0.001)
+ return connected_output, unconnected_output
+end
+
+# === IMPLICIT EXTRACTION ===
+if CONNECTIVITY_RULES
+ logger.info("Connectivity rules enabled, Netlist object will be generated.")
+ netlist
+end #CONNECTIVITY_RULES
+
+# === LAYOUT EXTENT ===
+CHIP = extent.sized(0.0)
+
+logger.info("Total area of the design is #{CHIP.area()} um^2.")
+
+
+
+#================================================
+#---------------------NWELL----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule NW.1a_3.3V: Min. Nwell Width (This is only for litho purpose on the generated area). is 0.86µm
+logger.info("Executing rule NW.1a_3.3V")
+nw1a_l1 = nwell.width(0.86.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+nw1a_l1.output("NW.1a_3.3V", "NW.1a_3.3V : Min. Nwell Width (This is only for litho purpose on the generated area). : 0.86µm")
+nw1a_l1.forget
+
+# Rule NW.1a_5V: Min. Nwell Width (This is only for litho purpose on the generated area). is 0.86µm
+logger.info("Executing rule NW.1a_5V")
+nw1a_l1 = nwell.width(0.86.um, euclidian).polygons(0.001).overlapping(dualgate)
+nw1a_l1.output("NW.1a_5V", "NW.1a_5V : Min. Nwell Width (This is only for litho purpose on the generated area). : 0.86µm")
+nw1a_l1.forget
+
+nw_1b = nwell.outside(dnwell).and(res_mk).not(comp).not(poly2)
+# Rule NW.1b_3.3V: Min. Nwell Width as a resistor (Outside DNWELL only). is 2µm
+logger.info("Executing rule NW.1b_3.3V")
+nw1b_l1 = nw_1b.width(2.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+nw1b_l1.output("NW.1b_3.3V", "NW.1b_3.3V : Min. Nwell Width as a resistor (Outside DNWELL only). : 2µm")
+nw1b_l1.forget
+
+# Rule NW.1b_5V: Min. Nwell Width as a resistor (Outside DNWELL only). is 2µm
+logger.info("Executing rule NW.1b_5V")
+nw1b_l1 = nw_1b.width(2.um, euclidian).polygons(0.001).overlapping(dualgate)
+nw1b_l1.output("NW.1b_5V", "NW.1b_5V : Min. Nwell Width as a resistor (Outside DNWELL only). : 2µm")
+nw1b_l1.forget
+
+if CONNECTIVITY_RULES
+logger.info("CONNECTIVITY_RULES section")
+
+connected_nwell_3p3v, unconnected_nwell_3p3v = conn_space(nwell, 0.6, 1.4, euclidian)
+
+connected_nwell_5p0v, unconnected_nwell_5p0v = conn_space(nwell, 0.74, 1.7, euclidian)
+
+# Rule NW.2a_3.3V: Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. is 0.6µm
+logger.info("Executing rule NW.2a_3.3V")
+nw2a_l1 = connected_nwell_3p3v.not_inside(ymtp_mk).not_interacting(v5_xtor).not_interacting(dualgate)
+nw2a_l1.output("NW.2a_3.3V", "NW.2a_3.3V : Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. : 0.6µm")
+nw2a_l1.forget
+
+# Rule NW.2a_5V: Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. is 0.74µm
+logger.info("Executing rule NW.2a_5V")
+nw2a_l1 = connected_nwell_5p0v.not_inside(ymtp_mk).overlapping(dualgate)
+nw2a_l1.output("NW.2a_5V", "NW.2a_5V : Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. : 0.74µm")
+nw2a_l1.forget
+
+# Rule NW.2b_3.3V: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.4µm
+logger.info("Executing rule NW.2b_3.3V")
+nw2b_l1 = unconnected_nwell_3p3v.not_interacting(v5_xtor).not_interacting(dualgate)
+nw2b_l1.output("NW.2b_3.3V", "NW.2b_3.3V : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.4µm")
+nw2b_l1.forget
+
+# Rule NW.2b_5V: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.7µm
+logger.info("Executing rule NW.2b_5V")
+nw2b_l1 = unconnected_nwell_5p0v.overlapping(dualgate)
+nw2b_l1.output("NW.2b_5V", "NW.2b_5V : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.7µm")
+nw2b_l1.forget
+
+else
+logger.info("CONNECTIVITY_RULES disabled section")
+
+# Rule NW.2b_3.3V_: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.4µm
+logger.info("Executing rule NW.2b_3.3V_")
+nw2b_l1 = nwell.isolated(1.4.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+nw2b_l1.output("NW.2b_3.3V_", "NW.2b_3.3V_ : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.4µm")
+nw2b_l1.forget
+
+# Rule NW.2b_5V_: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.7µm
+logger.info("Executing rule NW.2b_5V_")
+nw2b_l1 = nwell.isolated(1.7.um, euclidian).polygons(0.001).overlapping(dualgate)
+nw2b_l1.output("NW.2b_5V_", "NW.2b_5V_ : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.7µm")
+nw2b_l1.forget
+
+end #CONNECTIVITY_RULES
+
+# Rule NW.3_3.3V: Min. Nwell to DNWELL space. is 3.1µm
+logger.info("Executing rule NW.3_3.3V")
+nw3_l1 = nwell.separation(dnwell, 3.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+nw3_l1.output("NW.3_3.3V", "NW.3_3.3V : Min. Nwell to DNWELL space. : 3.1µm")
+nw3_l1.forget
+
+# Rule NW.3_5V: Min. Nwell to DNWELL space. is 3.1µm
+logger.info("Executing rule NW.3_5V")
+nw3_l1 = nwell.separation(dnwell, 3.1.um, euclidian).polygons(0.001).overlapping(dualgate)
+nw3_l1.output("NW.3_5V", "NW.3_5V : Min. Nwell to DNWELL space. : 3.1µm")
+nw3_l1.forget
+
+# Rule NW.4_3.3V: Min. Nwell to LVPWELL space.
+logger.info("Executing rule NW.4_3.3V")
+nw4_l1 = nwell.not_outside(lvpwell).not_interacting(v5_xtor).not_interacting(dualgate)
+nw4_l1.output("NW.4_3.3V", "NW.4_3.3V : Min. Nwell to LVPWELL space.")
+nw4_l1.forget
+
+# Rule NW.4_5V: Min. Nwell to LVPWELL space.
+logger.info("Executing rule NW.4_5V")
+nw4_l1 = nwell.not_outside(lvpwell).overlapping(dualgate)
+nw4_l1.output("NW.4_5V", "NW.4_5V : Min. Nwell to LVPWELL space.")
+nw4_l1.forget
+
+# Rule NW.5_3.3V: Min. DNWELL enclose Nwell. is 0.5µm
+logger.info("Executing rule NW.5_3.3V")
+nw5_l1 = dnwell.enclosing(nwell, 0.5.um, euclidian).polygons(0.001)
+nw5_l2 = nwell.not_outside(dnwell).not(dnwell)
+nw5_l = nw5_l1.or(nw5_l2).not_interacting(v5_xtor).not_interacting(dualgate)
+nw5_l.output("NW.5_3.3V", "NW.5_3.3V : Min. DNWELL enclose Nwell. : 0.5µm")
+nw5_l1.forget
+nw5_l2.forget
+nw5_l.forget
+
+# Rule NW.5_5V: Min. DNWELL enclose Nwell. is 0.5µm
+logger.info("Executing rule NW.5_5V")
+nw5_l1 = dnwell.enclosing(nwell, 0.5.um, euclidian).polygons(0.001)
+nw5_l2 = nwell.not_outside(dnwell).not(dnwell)
+nw5_l = nw5_l1.or(nw5_l2).overlapping(dualgate)
+nw5_l.output("NW.5_5V", "NW.5_5V : Min. DNWELL enclose Nwell. : 0.5µm")
+nw5_l1.forget
+nw5_l2.forget
+nw5_l.forget
+
+# Rule NW.6: Nwell resistors can only exist outside DNWELL.
+logger.info("Executing rule NW.6")
+nw6_l1 = nwell.inside(res_mk).interacting(dnwell)
+nw6_l1.output("NW.6", "NW.6 : Nwell resistors can only exist outside DNWELL.")
+nw6_l1.forget
+
+# rule NW.6_5V is not a DRC check
+
+# rule NW.7_3.3V is not a DRC check
+
+# rule NW.7_5V is not a DRC check
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/otp_mk.drc b/rules/klayout/drc/rule_decks/otp_mk.drc
new file mode 100644
index 0000000..32bb324
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/otp_mk.drc
@@ -0,0 +1,323 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (OTP_MK) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+otp_mk = polygons(173, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------OTP_MK---------------------
+#================================================
+
+# Rule O.DF.3a: Min. COMP Space. P-substrate tap (PCOMP outside NWELL) can be butted for different voltage devices as the potential is same. is 0.24µm
+logger.info("Executing rule O.DF.3a")
+odf3a_l1 = comp.and(otp_mk).space(0.24.um, euclidian).polygons(0.001)
+odf3a_l1.output("O.DF.3a", "O.DF.3a : Min. COMP Space. P-substrate tap (PCOMP outside NWELL) can be butted for different voltage devices as the potential is same. : 0.24µm")
+odf3a_l1.forget
+
+# Rule O.DF.6: Min. COMP extend beyond poly2 (it also means source/drain overhang). is 0.22µm
+logger.info("Executing rule O.DF.6")
+odf6_l1 = comp.and(otp_mk).enclosing(poly2.and(otp_mk), 0.22.um, euclidian).polygons(0.001)
+odf6_l1.output("O.DF.6", "O.DF.6 : Min. COMP extend beyond poly2 (it also means source/drain overhang). : 0.22µm")
+odf6_l1.forget
+
+# Rule O.DF.9: Min. COMP area (um2). is 0.1444µm²
+logger.info("Executing rule O.DF.9")
+odf9_l1 = comp.and(otp_mk).with_area(nil, 0.1444.um)
+odf9_l1.output("O.DF.9", "O.DF.9 : Min. COMP area (um2). : 0.1444µm²")
+odf9_l1.forget
+# Rule O.PL.2: Min. poly2 width. is 0.22µm
+logger.info("Executing rule O.PL.2")
+opl2_l1 = poly2.edges.and(tgate.edges).and(otp_mk).width(0.22.um, euclidian).polygons(0.001)
+opl2_l1.output("O.PL.2", "O.PL.2 : Min. poly2 width. : 0.22µm")
+opl2_l1.forget
+
+# Rule O.PL.3a: Min. poly2 Space on COMP. is 0.18µm
+logger.info("Executing rule O.PL.3a")
+opl3a_l1 = (tgate).or(poly2.not(comp)).and(otp_mk).space(0.18.um, euclidian).polygons(0.001)
+opl3a_l1.output("O.PL.3a", "O.PL.3a : Min. poly2 Space on COMP. : 0.18µm")
+opl3a_l1.forget
+
+# Rule O.PL.4: Min. extension beyond COMP to form Poly2 end cap. is 0.14µm
+logger.info("Executing rule O.PL.4")
+opl4_l1 = poly2.and(otp_mk).enclosing(comp.and(otp_mk), 0.14.um, euclidian).polygons(0.001)
+opl4_l1.output("O.PL.4", "O.PL.4 : Min. extension beyond COMP to form Poly2 end cap. : 0.14µm")
+opl4_l1.forget
+
+# rule O.PL.5a is not a DRC check
+
+# rule O.PL.5b is not a DRC check
+
+# Rule O.SB.2: Min. salicide Block Space. is 0.28µm
+logger.info("Executing rule O.SB.2")
+osb2_l1 = sab.and(otp_mk).space(0.28.um, euclidian).polygons(0.001)
+osb2_l1.output("O.SB.2", "O.SB.2 : Min. salicide Block Space. : 0.28µm")
+osb2_l1.forget
+
+# Rule O.SB.3: Min. space from salicide block to unrelated COMP. is 0.09µm
+logger.info("Executing rule O.SB.3")
+osb3_l1 = sab.outside(comp).and(otp_mk).separation(comp.outside(sab), 0.09.um, euclidian).polygons(0.001)
+osb3_l1.output("O.SB.3", "O.SB.3 : Min. space from salicide block to unrelated COMP. : 0.09µm")
+osb3_l1.forget
+
+# Rule O.SB.4: Min. space from salicide block to contact.
+logger.info("Executing rule O.SB.4")
+osb4_l1 = sab.and(otp_mk).separation(contact, 0.03.um, euclidian).polygons(0.001).or(sab.and(otp_mk).and(contact))
+osb4_l1.output("O.SB.4", "O.SB.4 : Min. space from salicide block to contact.")
+osb4_l1.forget
+
+# rule O.SB.5a is not a DRC check
+
+# Rule O.SB.5b_3.3V: Min. space from salicide block to unrelated Poly2 on COMP. is 0.1µm
+logger.info("Executing rule O.SB.5b_3.3V")
+osb5b_l1 = sab.outside(tgate).and(otp_mk).separation(tgate.outside(sab), 0.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+osb5b_l1.output("O.SB.5b_3.3V", "O.SB.5b_3.3V : Min. space from salicide block to unrelated Poly2 on COMP. : 0.1µm")
+osb5b_l1.forget
+
+# rule O.SB.5b_5V is not a DRC check
+
+# Rule O.SB.9: Min. salicide block extension beyond unsalicided Poly2. is 0.1µm
+logger.info("Executing rule O.SB.9")
+osb9_l1 = sab.and(otp_mk).enclosing(poly2.and(sab), 0.1.um, euclidian).polygons
+osb9_l1.output("O.SB.9", "O.SB.9 : Min. salicide block extension beyond unsalicided Poly2. : 0.1µm")
+osb9_l1.forget
+
+# Rule O.SB.11: Min. salicide block overlap with COMP. is 0.04µm
+logger.info("Executing rule O.SB.11")
+osb11_l1 = sab.and(otp_mk).overlap(comp, 0.04.um, euclidian).polygons
+osb11_l1.output("O.SB.11", "O.SB.11 : Min. salicide block overlap with COMP. : 0.04µm")
+osb11_l1.forget
+
+# rule O.SB.12 is not a DRC check
+
+# Rule O.SB.13_3.3V: Min. area of silicide block (um2). is 1.488µm²
+logger.info("Executing rule O.SB.13_3.3V")
+osb13_l1 = sab.and(otp_mk).with_area(nil, 1.488.um).not_interacting(v5_xtor).not_interacting(dualgate)
+osb13_l1.output("O.SB.13_3.3V", "O.SB.13_3.3V : Min. area of silicide block (um2). : 1.488µm²")
+osb13_l1.forget
+# Rule O.SB.13_5V: Min. area of silicide block (um2). is 2µm²
+logger.info("Executing rule O.SB.13_5V")
+osb13_l1 = sab.and(otp_mk).and(v5_xtor).with_area(nil, 2.um)
+osb13_l1.output("O.SB.13_5V", "O.SB.13_5V : Min. area of silicide block (um2). : 2µm²")
+osb13_l1.forget
+# rule O.SB.15b is not a DRC check
+
+# Rule O.CO.7: Min. space from COMP contact to Poly2 on COMP. is 0.13µm
+logger.info("Executing rule O.CO.7")
+oco7_l1 = contact.not_outside(comp).and(otp_mk).separation(tgate.and(otp_mk), 0.13.um, euclidian).polygons(0.001)
+oco7_l1.output("O.CO.7", "O.CO.7 : Min. space from COMP contact to Poly2 on COMP. : 0.13µm")
+oco7_l1.forget
+
+# Rule O.PL.ORT: Orientation-restricted gates must have the gate width aligned along the X-axis (poly line running horizontally) in reference to wafer notch down. is 0µm
+logger.info("Executing rule O.PL.ORT")
+oplort_l1 = comp.not(poly2).edges.and(tgate.edges).and(otp_mk).without_angle(0.um).extended(0, 0, 0.001, 0.001)
+oplort_l1.output("O.PL.ORT", "O.PL.ORT : Orientation-restricted gates must have the gate width aligned along the X-axis (poly line running horizontally) in reference to wafer notch down. : 0µm")
+oplort_l1.forget
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/p+_poly_resistor.drc b/rules/klayout/drc/rule_decks/p+_poly_resistor.drc
new file mode 100644
index 0000000..fc57577
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/p+_poly_resistor.drc
@@ -0,0 +1,285 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (P+ POLY RESISTOR) -------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+sab = polygons(49 , 0 )
+contact = polygons(33 , 0 )
+resistor = polygons(62 , 0 )
+res_mk = polygons(110, 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------P+ POLY RESISTOR----------------
+#================================================
+
+pres_poly = poly2.and(pplus).interacting(sab).interacting(res_mk).not_interacting(resistor)
+# Rule PRES.1: Minimum width of Poly2 resistor. is 0.8µm
+logger.info("Executing rule PRES.1")
+pres1_l1 = pres_poly.width(0.8.um, euclidian).polygons(0.001)
+pres1_l1.output("PRES.1", "PRES.1 : Minimum width of Poly2 resistor. : 0.8µm")
+pres1_l1.forget
+
+# Rule PRES.2: Minimum space between Poly2 resistors. is 0.4µm
+logger.info("Executing rule PRES.2")
+pres2_l1 = pres_poly.isolated(0.4.um, euclidian).polygons(0.001)
+pres2_l1.output("PRES.2", "PRES.2 : Minimum space between Poly2 resistors. : 0.4µm")
+pres2_l1.forget
+
+# Rule PRES.3: Minimum space from Poly2 resistor to COMP.
+logger.info("Executing rule PRES.3")
+pres3_l1 = pres_poly.separation(comp, 0.6.um, euclidian).polygons(0.001).or(comp.not_outside(pres_poly))
+pres3_l1.output("PRES.3", "PRES.3 : Minimum space from Poly2 resistor to COMP.")
+pres3_l1.forget
+
+# Rule PRES.4: Minimum space from Poly2 resistor to unrelated Poly2. is 0.6µm
+logger.info("Executing rule PRES.4")
+pres4_l1 = pres_poly.separation(poly2.not_interacting(sab), 0.6.um, euclidian).polygons(0.001)
+pres4_l1.output("PRES.4", "PRES.4 : Minimum space from Poly2 resistor to unrelated Poly2. : 0.6µm")
+pres4_l1.forget
+
+# Rule PRES.5: Minimum Plus implant overlap of Poly2 resistor. is 0.3µm
+logger.info("Executing rule PRES.5")
+pres5_l1 = pplus.enclosing(pres_poly, 0.3.um, euclidian).polygons(0.001)
+pres5_l2 = pres_poly.not_outside(pplus).not(pplus)
+pres5_l = pres5_l1.or(pres5_l2)
+pres5_l.output("PRES.5", "PRES.5 : Minimum Plus implant overlap of Poly2 resistor. : 0.3µm")
+pres5_l1.forget
+pres5_l2.forget
+pres5_l.forget
+
+# Rule PRES.6: Minimum salicide block overlap of Poly2 resistor in width direction. is 0.28µm
+logger.info("Executing rule PRES.6")
+pres6_l1 = sab.enclosing(pres_poly,0.28.um).polygons(0.001)
+pres6_l1.output("PRES.6", "PRES.6 : Minimum salicide block overlap of Poly2 resistor in width direction. : 0.28µm")
+pres6_l1.forget
+
+# Rule PRES.7: Space from salicide block to contact on Poly2 resistor.
+logger.info("Executing rule PRES.7")
+pres7_l1 = contact.inside(pres_poly).separation(sab,0.22.um).polygons(0.001).or(contact.inside(pres_poly).interacting(sab))
+pres7_l1.output("PRES.7", "PRES.7 : Space from salicide block to contact on Poly2 resistor.")
+pres7_l1.forget
+
+# rule PRES.8 is not a DRC check
+
+mk_pres9a = res_mk.edges.not(poly2.and(pplus).and(sab).edges).inside_part(poly2)
+# Rule PRES.9a: Pplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.
+logger.info("Executing rule PRES.9a")
+pres9a_l1 = res_mk.interacting(pres_poly).interacting(mk_pres9a)
+pres9a_l1.output("PRES.9a", "PRES.9a : Pplus Poly2 resistor shall be covered by RES_MK marking. RES_MK length shall be coincide with resistor length (Defined by SAB length) and width covering the width of Poly2.")
+pres9a_l1.forget
+
+mk_pres9a.forget
+pres9b = res_mk.with_area(15000.01.um,nil).in(res_mk.interacting(res_mk.edges.with_length(80.01.um,nil)))
+# Rule PRES.9b: If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. is 20µm
+logger.info("Executing rule PRES.9b")
+pres9b_l1 = pres9b.interacting(pres_poly).drc(separation(pres9b) < 20.um).polygons(0.001)
+pres9b_l1.output("PRES.9b", "PRES.9b : If the size of single RES_MK mark layer is greater than 15000um2 and both side (X and Y) are greater than 80um. then the minimum spacing to adjacent RES_MK layer. : 20µm")
+pres9b_l1.forget
+
+pres9b.forget
+pres_poly.forget
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/poly2.drc b/rules/klayout/drc/rule_decks/poly2.drc
new file mode 100644
index 0000000..adf8610
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/poly2.drc
@@ -0,0 +1,373 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (POLY2) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+mvsd = polygons(210, 0 )
+mvpsd = polygons(11 , 39)
+res_mk = polygons(110, 5 )
+otp_mk = polygons(173, 5 )
+sramcore = polygons(108, 5 )
+plfuse = polygons(125, 5 )
+ymtp_mk = polygons(86 , 17)
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------POLY2----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule PL.1_3.3V: Interconnect Width (outside PLFUSE). is 0.18µm
+logger.info("Executing rule PL.1_3.3V")
+pl1_l1 = poly2.outside(plfuse).not(ymtp_mk).width(0.18.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl1_l1.output("PL.1_3.3V", "PL.1_3.3V : Interconnect Width (outside PLFUSE). : 0.18µm")
+pl1_l1.forget
+
+# Rule PL.1_5V: Interconnect Width (outside PLFUSE). is 0.2µm
+logger.info("Executing rule PL.1_5V")
+pl1_l1 = poly2.outside(plfuse).not(ymtp_mk).width(0.2.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl1_l1.output("PL.1_5V", "PL.1_5V : Interconnect Width (outside PLFUSE). : 0.2µm")
+pl1_l1.forget
+
+# Rule PL.1a_3.3V: Interconnect Width (inside PLFUSE). is 0.18µm
+logger.info("Executing rule PL.1a_3.3V")
+pl1a_l1 = poly2.inside(plfuse).width(0.18.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl1a_l1.output("PL.1a_3.3V", "PL.1a_3.3V : Interconnect Width (inside PLFUSE). : 0.18µm")
+pl1a_l1.forget
+
+# Rule PL.1a_5V: Interconnect Width (inside PLFUSE). is 0.18µm
+logger.info("Executing rule PL.1a_5V")
+pl1a_l1 = poly2.inside(plfuse).width(0.18.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl1a_l1.output("PL.1a_5V", "PL.1a_5V : Interconnect Width (inside PLFUSE). : 0.18µm")
+pl1a_l1.forget
+
+# Rule PL.2_3.3V: Gate Width (Channel Length). is 0.28µm
+logger.info("Executing rule PL.2_3.3V")
+pl2_l1 = poly2.edges.and(tgate.edges).not(otp_mk).not(ymtp_mk).width(0.28.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl2_l1.output("PL.2_3.3V", "PL.2_3.3V : Gate Width (Channel Length). : 0.28µm")
+pl2_l1.forget
+
+pl_2_5v_n = comp.not(poly2).edges.and(ngate.edges).and(v5_xtor).and(dualgate).space(0.6.um, euclidian).polygons
+pl_2_5v_p = comp.not(poly2).edges.and(pgate.edges).and(v5_xtor).and(dualgate).space(0.5.um, euclidian).polygons
+pl_2_6v_n = comp.not(poly2).edges.and(ngate.edges).not(v5_xtor).and(dualgate).space(0.7.um, euclidian).polygons
+pl_2_6v_p = comp.not(poly2).edges.and(pgate.edges).not(v5_xtor).and(dualgate).space(0.55.um, euclidian).polygons
+# Rule PL.2_5V: Gate Width (Channel Length).
+logger.info("Executing rule PL.2_5V")
+pl2_l1 = pl_2_5v_n.or(pl_2_5v_p).or(pl_2_6v_n.or(pl_2_6v_p))
+pl2_l1.output("PL.2_5V", "PL.2_5V : Gate Width (Channel Length).")
+pl2_l1.forget
+
+pl_2_5v_n.forget
+pl_2_5v_p.forget
+pl_2_6v_n.forget
+pl_2_6v_p.forget
+# Rule PL.3a_3.3V: Space on COMP/Field. is 0.24µm
+logger.info("Executing rule PL.3a_3.3V")
+pl3a_l1 = (tgate).or(poly2.not(comp)).not(otp_mk).space(0.24.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl3a_l1.output("PL.3a_3.3V", "PL.3a_3.3V : Space on COMP/Field. : 0.24µm")
+pl3a_l1.forget
+
+# Rule PL.3a_5V: Space on COMP/Field. is 0.24µm
+logger.info("Executing rule PL.3a_5V")
+pl3a_l1 = (tgate).or(poly2.not(comp)).not(otp_mk).space(0.24.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl3a_l1.output("PL.3a_5V", "PL.3a_5V : Space on COMP/Field. : 0.24µm")
+pl3a_l1.forget
+
+# rule PL.3b_3.3V is not a DRC check
+
+# rule PL.3b_5V is not a DRC check
+
+poly_pl = poly2.not(otp_mk).not(ymtp_mk).not(mvsd).not(mvpsd)
+comp_pl = comp.not(otp_mk).not(ymtp_mk).not(mvsd).not(mvpsd)
+# Rule PL.4_3.3V: Extension beyond COMP to form Poly2 end cap. is 0.22µm
+logger.info("Executing rule PL.4_3.3V")
+pl4_l1 = poly_pl.enclosing(comp.not(otp_mk).not(ymtp_mk), 0.22.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl4_l1.output("PL.4_3.3V", "PL.4_3.3V : Extension beyond COMP to form Poly2 end cap. : 0.22µm")
+pl4_l1.forget
+
+# Rule PL.4_5V: Extension beyond COMP to form Poly2 end cap. is 0.22µm
+logger.info("Executing rule PL.4_5V")
+pl4_l1 = poly_pl.enclosing(comp.not(otp_mk).not(ymtp_mk), 0.22.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl4_l1.output("PL.4_5V", "PL.4_5V : Extension beyond COMP to form Poly2 end cap. : 0.22µm")
+pl4_l1.forget
+
+# Rule PL.5a_3.3V: Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. is 0.1µm
+logger.info("Executing rule PL.5a_3.3V")
+pl5a_l1 = poly_pl.separation(comp_pl, 0.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl5a_l1.output("PL.5a_3.3V", "PL.5a_3.3V : Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. : 0.1µm")
+pl5a_l1.forget
+
+# Rule PL.5a_5V: Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. is 0.3µm
+logger.info("Executing rule PL.5a_5V")
+pl5a_l1 = poly_pl.outside(sramcore).separation(comp_pl, 0.3.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl5a_l1.output("PL.5a_5V", "PL.5a_5V : Space from field Poly2 to unrelated COMP Spacer from field Poly2 to Guard-ring. : 0.3µm")
+pl5a_l1.forget
+
+# Rule PL.5b_3.3V: Space from field Poly2 to related COMP. is 0.1µm
+logger.info("Executing rule PL.5b_3.3V")
+pl5b_l1 = poly_pl.separation(comp_pl, 0.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl5b_l1.output("PL.5b_3.3V", "PL.5b_3.3V : Space from field Poly2 to related COMP. : 0.1µm")
+pl5b_l1.forget
+
+# Rule PL.5b_5V: Space from field Poly2 to related COMP. is 0.3µm
+logger.info("Executing rule PL.5b_5V")
+pl5b_l1 = poly_pl.outside(sramcore).separation(comp_pl, 0.3.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl5b_l1.output("PL.5b_5V", "PL.5b_5V : Space from field Poly2 to related COMP. : 0.3µm")
+pl5b_l1.forget
+
+poly_pl.forget
+comp_pl.forget
+poly_90deg = poly2.corners(90.0).sized(0.1).or(poly2.corners(-90.0).sized(0.1)).not(ymtp_mk)
+# Rule PL.6: 90 degree bends on the COMP are not allowed.
+logger.info("Executing rule PL.6")
+pl6_l1 = poly2.corners(90.0).sized(0.1).or(poly2.corners(-90.0).sized(0.1)).not(ymtp_mk).inside(comp.not(ymtp_mk))
+pl6_l1.output("PL.6", "PL.6 : 90 degree bends on the COMP are not allowed.")
+pl6_l1.forget
+
+poly_90deg.forget
+poly_45deg = poly2.edges.with_angle(-45).or(poly2.edges.with_angle(45))
+# Rule PL.7_3.3V: 45 degree bent gate width is 0.3µm
+logger.info("Executing rule PL.7_3.3V")
+pl7_l1 = poly_45deg.width(0.3.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+pl7_l1.output("PL.7_3.3V", "PL.7_3.3V : 45 degree bent gate width : 0.3µm")
+pl7_l1.forget
+
+# Rule PL.7_5V: 45 degree bent gate width is 0.7µm
+logger.info("Executing rule PL.7_5V")
+pl7_l1 = poly_45deg.width(0.7.um, euclidian).polygons(0.001).overlapping(dualgate)
+pl7_l1.output("PL.7_5V", "PL.7_5V : 45 degree bent gate width : 0.7µm")
+pl7_l1.forget
+
+poly_45deg.forget
+# Rule PL.9: Poly2 inter connect connecting 3.3V and 5V areas (area inside and outside Dualgate) are not allowed. They shall be done though metal lines only.
+logger.info("Executing rule PL.9")
+pl9_l1 = poly2.interacting(poly2.not(v5_xtor).not(dualgate)).interacting(poly2.and(dualgate))
+pl9_l1.output("PL.9", "PL.9 : Poly2 inter connect connecting 3.3V and 5V areas (area inside and outside Dualgate) are not allowed. They shall be done though metal lines only.")
+pl9_l1.forget
+
+# rule PL.10 is not a DRC check
+
+# Rule PL.11: V5_Xtor must enclose 5V device.
+logger.info("Executing rule PL.11")
+pl11_l1 = v5_xtor.not_interacting(dualgate.or(otp_mk))
+pl11_l1.output("PL.11", "PL.11 : V5_Xtor must enclose 5V device.")
+pl11_l1.forget
+
+# rule PL.12_3.3V is not a DRC check
+
+# Rule PL.12: V5_Xtor enclose 5V Comp.
+logger.info("Executing rule PL.12")
+pl12_l1 = comp.interacting(v5_xtor).not(v5_xtor)
+pl12_l1.output("PL.12", "PL.12 : V5_Xtor enclose 5V Comp.")
+pl12_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/pplus.drc b/rules/klayout/drc/rule_decks/pplus.drc
new file mode 100644
index 0000000..98f534a
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/pplus.drc
@@ -0,0 +1,421 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (PPLUS) ------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+lvpwell = polygons(204, 0 )
+sab = polygons(49 , 0 )
+resistor = polygons(62 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#---------------------PPLUS----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule PP.1: min. pplus width is 0.4µm
+logger.info("Executing rule PP.1")
+pp1_l1 = pplus.width(0.4.um, euclidian).polygons(0.001)
+pp1_l1.output("PP.1", "PP.1 : min. pplus width : 0.4µm")
+pp1_l1.forget
+
+# Rule PP.2: min. pplus spacing is 0.4µm
+logger.info("Executing rule PP.2")
+pp2_l1 = pplus.space(0.4.um, euclidian).polygons(0.001)
+pp2_l1.output("PP.2", "PP.2 : min. pplus spacing : 0.4µm")
+pp2_l1.forget
+
+# Rule PP.3a: Space to NCOMP for NCOMP (1) inside LVPWELL (2) outside NWELL and DNWELL. is 0.16µm
+logger.info("Executing rule PP.3a")
+pp3a_l1 = pplus.separation((ncomp.inside(lvpwell)).or(ncomp.outside(nwell).outside(dnwell)), 0.16.um, euclidian).polygons(0.001)
+pp3a_l1.output("PP.3a", "PP.3a : Space to NCOMP for NCOMP (1) inside LVPWELL (2) outside NWELL and DNWELL. : 0.16µm")
+pp3a_l1.forget
+
+pp_3bi = ncomp.edges.not(lvpwell.inside(dnwell).sized(0.429.um))
+# Rule PP.3bi: Space to NCOMP: For Inside DNWELL. (i) NCOMP space to LVPWELL >= 0.43um. is 0.08µm
+logger.info("Executing rule PP.3bi")
+pp3bi_l1 = pplus.inside(dnwell).edges.separation(pp_3bi, 0.08.um, euclidian).polygons(0.001)
+pp3bi_l1.output("PP.3bi", "PP.3bi : Space to NCOMP: For Inside DNWELL. (i) NCOMP space to LVPWELL >= 0.43um. : 0.08µm")
+pp3bi_l1.forget
+
+pp_3bi.forget
+pp_3bii = ncomp.edges.and(lvpwell.inside(dnwell).sized(0.429.um))
+# Rule PP.3bii: Space to NCOMP: For Inside DNWELL. (ii) NCOMP space to LVPWELL < 0.43um. is 0.16µm
+logger.info("Executing rule PP.3bii")
+pp3bii_l1 = pplus.inside(dnwell).edges.separation(pp_3bii, 0.16.um, euclidian).polygons(0.001)
+pp3bii_l1.output("PP.3bii", "PP.3bii : Space to NCOMP: For Inside DNWELL. (ii) NCOMP space to LVPWELL < 0.43um. : 0.16µm")
+pp3bii_l1.forget
+
+pp_3bii.forget
+pp_3ci_extend = nwell.outside(dnwell).sized(-0.429.um)
+pp_3ci = ncomp.edges.and(pp_3ci_extend)
+# Rule PP.3ci: Space to NCOMP: For Outside DNWELL, inside Nwell: (i) NWELL Overlap of NCOMP >= 0.43um. is 0.08µm
+logger.info("Executing rule PP.3ci")
+pp3ci_l1 = pplus.outside(dnwell).edges.separation(pp_3ci, 0.08.um, euclidian).polygons(0.001)
+pp3ci_l1.output("PP.3ci", "PP.3ci : Space to NCOMP: For Outside DNWELL, inside Nwell: (i) NWELL Overlap of NCOMP >= 0.43um. : 0.08µm")
+pp3ci_l1.forget
+
+pp_3ci_extend.forget
+pp_3ci.forget
+pp_3cii_extend = nwell.outside(dnwell).not(nwell.outside(dnwell).sized(-0.429.um))
+pp_3cii = ncomp.edges.and(pp_3cii_extend)
+# Rule PP.3cii: Space to NCOMP: For Outside DNWELL, inside Nwell: (ii) NWELL Overlap of NCOMP 0.43um. is 0.16µm
+logger.info("Executing rule PP.3cii")
+pp3cii_l1 = pplus.outside(dnwell).edges.separation(pp_3cii, 0.16.um, euclidian).polygons(0.001)
+pp3cii_l1.output("PP.3cii", "PP.3cii : Space to NCOMP: For Outside DNWELL, inside Nwell: (ii) NWELL Overlap of NCOMP 0.43um. : 0.16µm")
+pp3cii_l1.forget
+
+pp_3cii_extend.forget
+pp_3cii.forget
+# Rule PP.3d: Min/max space to a butted NCOMP.
+logger.info("Executing rule PP.3d")
+pp3d_l1 = pplus.not_outside(ncomp)
+pp3d_l1.output("PP.3d", "PP.3d : Min/max space to a butted NCOMP.")
+pp3d_l1.forget
+
+# Rule PP.3e: Space to NCOMP edge adjacent to a butting edge.
+logger.info("Executing rule PP.3e")
+pp3e_l1 = pplus.not_outside(ncomp)
+pp3e_l1.output("PP.3e", "PP.3e : Space to NCOMP edge adjacent to a butting edge.")
+pp3e_l1.forget
+
+# Rule PP.4a: Space related to N-channel gate at a butting edge parallel to gate. is 0.32µm
+logger.info("Executing rule PP.4a")
+pp4a_l1 = pplus.edges.and(ncomp.edges).separation(ngate.edges, 0.32.um, projection).polygons(0.001)
+pp4a_l1.output("PP.4a", "PP.4a : Space related to N-channel gate at a butting edge parallel to gate. : 0.32µm")
+pp4a_l1.forget
+
+pp_4b_poly = poly2.edges.interacting(ngate.edges.not(ncomp.edges)).centers(0, 0.99).and(ngate.sized(0.32.um))
+# Rule PP.4b: Within 0.32um of channel, space to N-channel gate extension perpendicular to the direction of Poly2.
+logger.info("Executing rule PP.4b")
+pp4b_l1 = pplus.interacting(pplus.edges.separation(pp_4b_poly, 0.22.um, projection).polygons(0.001))
+pp4b_l1.output("PP.4b", "PP.4b : Within 0.32um of channel, space to N-channel gate extension perpendicular to the direction of Poly2.")
+pp4b_l1.forget
+
+pp_4b_poly.forget
+# Rule PP.5a: Overlap of P-channel gate. is 0.23µm
+logger.info("Executing rule PP.5a")
+pp5a_l1 = pplus.enclosing(pgate, 0.23.um, euclidian).polygons(0.001)
+pp5a_l2 = pgate.not_outside(pplus).not(pplus)
+pp5a_l = pp5a_l1.or(pp5a_l2)
+pp5a_l.output("PP.5a", "PP.5a : Overlap of P-channel gate. : 0.23µm")
+pp5a_l1.forget
+pp5a_l2.forget
+pp5a_l.forget
+
+# Rule PP.5b: Extension beyond COMP for COMP (1) Inside NWELL (2) outside LVPWELL but inside DNWELL. is 0.16µm
+logger.info("Executing rule PP.5b")
+pp5b_l1 = pplus.not_outside(nwell).or(pplus.outside(lvpwell).inside(dnwell)).edges.not(nplus).enclosing(comp.edges, 0.16.um, euclidian).polygons(0.001)
+pp5b_l1.output("PP.5b", "PP.5b : Extension beyond COMP for COMP (1) Inside NWELL (2) outside LVPWELL but inside DNWELL. : 0.16µm")
+pp5b_l1.forget
+
+pp_5ci_background = pplus.not_outside(lvpwell).inside(dnwell).edges.not(nplus.edges)
+pp_5ci_extend = lvpwell.inside(dnwell).sized(-0.429.um)
+pp_5ci_foreground = pcomp.not_outside(lvpwell).inside(dnwell).edges.not(nplus.edges).inside_part(pp_5ci_extend)
+# Rule PP.5ci: Extension beyond COMP: For Inside DNWELL, inside LVPWELL: (i) For LVPWELL overlap of Pplus >= 0.43um for LVPWELL tap. is 0.02µm
+logger.info("Executing rule PP.5ci")
+pp5ci_l1 = pp_5ci_background.enclosing(pp_5ci_foreground, 0.02.um, euclidian).polygons(0.001)
+pp5ci_l1.output("PP.5ci", "PP.5ci : Extension beyond COMP: For Inside DNWELL, inside LVPWELL: (i) For LVPWELL overlap of Pplus >= 0.43um for LVPWELL tap. : 0.02µm")
+pp5ci_l1.forget
+
+pp_5ci_background.forget
+pp_5ci_extend.forget
+pp_5ci_foreground.forget
+pp_5cii_background = pplus.not_outside(lvpwell).inside(dnwell).edges
+pp_5cii_extend = lvpwell.inside(dnwell).not(lvpwell.inside(dnwell).sized(-0.429.um))
+pp_5cii_foreground = pcomp.not_outside(lvpwell).inside(dnwell).edges.not(nplus.edges).and(pp_5cii_extend)
+# Rule PP.5cii: Extension beyond COMP: For Inside DNWELL, inside LVPWELL: (ii) For LVPWELL overlap of Pplus < 0.43um for the LVPWELL tap. is 0.16µm
+logger.info("Executing rule PP.5cii")
+pp5cii_l1 = pp_5cii_background.enclosing(pp_5cii_foreground, 0.16.um, projection).polygons(0.001)
+pp5cii_l1.output("PP.5cii", "PP.5cii : Extension beyond COMP: For Inside DNWELL, inside LVPWELL: (ii) For LVPWELL overlap of Pplus < 0.43um for the LVPWELL tap. : 0.16µm")
+pp5cii_l1.forget
+
+pp_5cii_background.forget
+pp_5cii_extend.forget
+pp_5cii_foreground.forget
+pp_5di_background = pplus.outside(dnwell).edges
+pp_5di_foreground = pcomp.outside(dnwell).edges.not(nplus.edges).not(nwell.outside(dnwell).sized(0.429.um))
+# Rule PP.5di: Extension beyond COMP: For Outside DNWELL (i) For Pplus to NWELL space >= 0.43um for Pfield or LVPWELL tap. is 0.02µm
+logger.info("Executing rule PP.5di")
+pp5di_l1 = pp_5di_background.enclosing(pp_5di_foreground, 0.02.um, projection).polygons(0.001)
+pp5di_l1.output("PP.5di", "PP.5di : Extension beyond COMP: For Outside DNWELL (i) For Pplus to NWELL space >= 0.43um for Pfield or LVPWELL tap. : 0.02µm")
+pp5di_l1.forget
+
+pp_5di_background.forget
+pp_5di_foreground.forget
+pp_5dii_background = pplus.outside(dnwell).edges
+pp_5dii_foreground = pcomp.outside(dnwell).edges.not(nplus.edges).and(nwell.outside(dnwell).sized(0.429.um))
+# Rule PP.5dii: Extension beyond COMP: For Outside DNWELL (ii) For Pplus to NWELL space < 0.43um for Pfield or LVPWELL tap. is 0.16µm
+logger.info("Executing rule PP.5dii")
+pp5dii_l1 = pp_5dii_background.enclosing(pp_5dii_foreground, 0.16.um, projection).polygons(0.001)
+pp5dii_l1.output("PP.5dii", "PP.5dii : Extension beyond COMP: For Outside DNWELL (ii) For Pplus to NWELL space < 0.43um for Pfield or LVPWELL tap. : 0.16µm")
+pp5dii_l1.forget
+
+pp_5dii_background.forget
+pp_5dii_foreground.forget
+# Rule PP.6: Overlap with PCOMP butted to NCOMP. is 0.22µm
+logger.info("Executing rule PP.6")
+pp6_l1 = comp.interacting(pplus).enclosing(ncomp.interacting(pplus), 0.22.um, projection).polygons
+pp6_l1.output("PP.6", "PP.6 : Overlap with PCOMP butted to NCOMP. : 0.22µm")
+pp6_l1.forget
+
+# Rule PP.7: Space to unrelated unsalicided Poly2. is 0.18µm
+logger.info("Executing rule PP.7")
+pp7_l1 = pplus.separation(poly2.and(sab), 0.18.um, euclidian).polygons(0.001)
+pp7_l1.output("PP.7", "PP.7 : Space to unrelated unsalicided Poly2. : 0.18µm")
+pp7_l1.forget
+
+# Rule PP.8a: Minimum Pplus area (um2). is 0.35µm²
+logger.info("Executing rule PP.8a")
+pp8a_l1 = pplus.with_area(nil, 0.35.um)
+pp8a_l1.output("PP.8a", "PP.8a : Minimum Pplus area (um2). : 0.35µm²")
+pp8a_l1.forget
+# Rule PP.8b: Minimum area enclosed by Pplus (um2). is 0.35µm²
+logger.info("Executing rule PP.8b")
+pp8b_l1 = pplus.holes.with_area(nil, 0.35.um)
+pp8b_l1.output("PP.8b", "PP.8b : Minimum area enclosed by Pplus (um2). : 0.35µm²")
+pp8b_l1.forget
+# Rule PP.9: Overlap of unsalicided Poly2. is 0.18µm
+logger.info("Executing rule PP.9")
+pp9_l1 = pplus.enclosing(poly2.not_interacting(resistor).and(sab), 0.18.um, euclidian).polygons(0.001)
+pp9_l2 = poly2.not_interacting(resistor).and(sab).not_outside(pplus).not(pplus)
+pp9_l = pp9_l1.or(pp9_l2)
+pp9_l.output("PP.9", "PP.9 : Overlap of unsalicided Poly2. : 0.18µm")
+pp9_l1.forget
+pp9_l2.forget
+pp9_l.forget
+
+# Rule PP.10: Overlap of unsalicided COMP. is 0.18µm
+logger.info("Executing rule PP.10")
+pp10_l1 = pplus.enclosing(comp.and(sab), 0.18.um, euclidian).polygons(0.001)
+pp10_l1.output("PP.10", "PP.10 : Overlap of unsalicided COMP. : 0.18µm")
+pp10_l1.forget
+
+pp_11_in_dnwell = pplus.interacting(pplus.edges.and(ncomp.edges).and(lvpwell.inside(dnwell).sized(0.429.um)))
+pp_11_out_dnwell = pplus.interacting(pplus.edges.and(ncomp.edges).and(nwell.outside(dnwell).not(nwell.outside(dnwell).sized(-0.429.um))))
+# Rule PP.11: Butting Pplus and NCOMP is forbidden within 0.43um of Nwell edge (for outside DNWELL) and of LVPWELL edge (for inside DNWELL case).
+logger.info("Executing rule PP.11")
+pp11_l1 = pp_11_in_dnwell.or(pp_11_out_dnwell)
+pp11_l1.output("PP.11", "PP.11 : Butting Pplus and NCOMP is forbidden within 0.43um of Nwell edge (for outside DNWELL) and of LVPWELL edge (for inside DNWELL case).")
+pp11_l1.forget
+
+pp_11_in_dnwell.forget
+pp_11_out_dnwell.forget
+# Rule PP.12: Overlap with N-channel Poly2 gate extension is forbidden within 0.32um of N-channel gate.
+logger.info("Executing rule PP.12")
+pp12_l1 = pplus.interacting(pplus.edges.separation(ngate.edges.and(ncomp.edges), 0.32.um, euclidian).polygons(0.001))
+pp12_l1.output("PP.12", "PP.12 : Overlap with N-channel Poly2 gate extension is forbidden within 0.32um of N-channel gate.")
+pp12_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/sab.drc b/rules/klayout/drc/rule_decks/sab.drc
new file mode 100644
index 0000000..387351e
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/sab.drc
@@ -0,0 +1,346 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (SAB) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+nwell = polygons(21 , 0 )
+sab = polygons(49 , 0 )
+esd = polygons(24 , 0 )
+contact = polygons(33 , 0 )
+otp_mk = polygons(173, 5 )
+lvs_io = polygons(119, 5 )
+esd_mk = polygons(24 , 5 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------SAB-----------------------
+#================================================
+
+
+if FEOL
+logger.info("FEOL section")
+
+# Rule SB.1: min. sab width is 0.42µm
+logger.info("Executing rule SB.1")
+sb1_l1 = sab.width(0.42.um, euclidian).polygons(0.001)
+sb1_l1.output("SB.1", "SB.1 : min. sab width : 0.42µm")
+sb1_l1.forget
+
+# Rule SB.2: min. sab spacing is 0.42µm
+logger.info("Executing rule SB.2")
+sb2_l1 = sab.outside(otp_mk).space(0.42.um, euclidian).polygons(0.001)
+sb2_l1.output("SB.2", "SB.2 : min. sab spacing : 0.42µm")
+sb2_l1.forget
+
+# Rule SB.3: Space from salicide block to unrelated COMP. is 0.22µm
+logger.info("Executing rule SB.3")
+sb3_l1 = sab.outside(comp).outside(otp_mk).separation(comp.outside(sab), 0.22.um, euclidian).polygons(0.001)
+sb3_l1.output("SB.3", "SB.3 : Space from salicide block to unrelated COMP. : 0.22µm")
+sb3_l1.forget
+
+# Rule SB.4: Space from salicide block to contact.
+logger.info("Executing rule SB.4")
+sb4_l1 = sab.outside(otp_mk).separation(contact, 0.15.um, euclidian).polygons(0.001).or(sab.outside(otp_mk).and(contact))
+sb4_l1.output("SB.4", "SB.4 : Space from salicide block to contact.")
+sb4_l1.forget
+
+# Rule SB.5a: Space from salicide block to unrelated Poly2 on field. is 0.3µm
+logger.info("Executing rule SB.5a")
+sb5a_l1 = sab.outside(poly2.not(comp)).outside(otp_mk).separation(poly2.not(comp).outside(sab), 0.3.um, euclidian).polygons(0.001)
+sb5a_l1.output("SB.5a", "SB.5a : Space from salicide block to unrelated Poly2 on field. : 0.3µm")
+sb5a_l1.forget
+
+# Rule SB.5b: Space from salicide block to unrelated Poly2 on COMP. is 0.28µm
+logger.info("Executing rule SB.5b")
+sb5b_l1 = sab.outside(tgate).outside(otp_mk).separation(tgate.outside(sab), 0.28.um, euclidian).polygons(0.001)
+sb5b_l1.output("SB.5b", "SB.5b : Space from salicide block to unrelated Poly2 on COMP. : 0.28µm")
+sb5b_l1.forget
+
+# Rule SB.6: Salicide block extension beyond related COMP. is 0.22µm
+logger.info("Executing rule SB.6")
+sb6_l1 = sab.enclosing(comp, 0.22.um, euclidian).polygons(0.001)
+sb6_l1.output("SB.6", "SB.6 : Salicide block extension beyond related COMP. : 0.22µm")
+sb6_l1.forget
+
+# Rule SB.7: COMP extension beyond related salicide block. is 0.22µm
+logger.info("Executing rule SB.7")
+sb7_l1 = comp.enclosing(sab, 0.22.um, euclidian).polygons
+sb7_l1.output("SB.7", "SB.7 : COMP extension beyond related salicide block. : 0.22µm")
+sb7_l1.forget
+
+# Rule SB.8: Non-salicided contacts are forbidden.
+logger.info("Executing rule SB.8")
+sb8_l1 = contact.inside(sab)
+sb8_l1.output("SB.8", "SB.8 : Non-salicided contacts are forbidden.")
+sb8_l1.forget
+
+# Rule SB.9: Salicide block extension beyond unsalicided Poly2. is 0.22µm
+logger.info("Executing rule SB.9")
+sb9_l1 = sab.outside(otp_mk).enclosing(poly2.and(sab), 0.22.um, euclidian).polygons
+sb9_l1.output("SB.9", "SB.9 : Salicide block extension beyond unsalicided Poly2. : 0.22µm")
+sb9_l1.forget
+
+# Rule SB.10: Poly2 extension beyond related salicide block. is 0.22µm
+logger.info("Executing rule SB.10")
+sb10_l1 = poly2.enclosing(sab, 0.22.um, euclidian).polygons(0.001)
+sb10_l1.output("SB.10", "SB.10 : Poly2 extension beyond related salicide block. : 0.22µm")
+sb10_l1.forget
+
+# Rule SB.11: Overlap with COMP. is 0.22µm
+logger.info("Executing rule SB.11")
+sb11_l1 = sab.outside(otp_mk).overlap(comp, 0.22.um, euclidian).polygons
+sb11_l1.output("SB.11", "SB.11 : Overlap with COMP. : 0.22µm")
+sb11_l1.forget
+
+# Rule SB.12: Overlap with Poly2 outside ESD_MK. is 0.22µm
+logger.info("Executing rule SB.12")
+sb12_l1 = sab.outside(otp_mk).outside(esd_mk).overlap(poly2.outside(otp_mk).outside(esd_mk), 0.22.um, euclidian).polygons
+sb12_l1.output("SB.12", "SB.12 : Overlap with Poly2 outside ESD_MK. : 0.22µm")
+sb12_l1.forget
+
+# Rule SB.13: Min. area (um2). is 2µm²
+logger.info("Executing rule SB.13")
+sb13_l1 = sab.outside(otp_mk).with_area(nil, 2.um)
+sb13_l1.output("SB.13", "SB.13 : Min. area (um2). : 2µm²")
+sb13_l1.forget
+# Rule SB.14a: Space from unsalicided Nplus Poly2 to unsalicided Pplus Poly2. (Unsalicided Nplus Poly2 must not fall within a square of 0.56um x 0.56um at unsalicided Pplus Poly2 corners). is 0.56µm
+logger.info("Executing rule SB.14a")
+sb14a_l1 = poly2.and(nplus).and(sab).separation(poly2.and(pplus).and(sab), 0.56.um, square).polygons
+sb14a_l1.output("SB.14a", "SB.14a : Space from unsalicided Nplus Poly2 to unsalicided Pplus Poly2. (Unsalicided Nplus Poly2 must not fall within a square of 0.56um x 0.56um at unsalicided Pplus Poly2 corners). : 0.56µm")
+sb14a_l1.forget
+
+# Rule SB.14b: Space from unsalicided Nplus Poly2 to P-channel gate. (Unsalicided Nplus Poly2 must not fall within a square of 0.56um x 0.56um at P-channel gate corners). is 0.56µm
+logger.info("Executing rule SB.14b")
+sb14b_l1 = poly2.and(nplus).and(sab).separation(pgate, 0.56.um, square).polygons
+sb14b_l1.output("SB.14b", "SB.14b : Space from unsalicided Nplus Poly2 to P-channel gate. (Unsalicided Nplus Poly2 must not fall within a square of 0.56um x 0.56um at P-channel gate corners). : 0.56µm")
+sb14b_l1.forget
+
+# Rule SB.15a: Space from unsalicided Poly2 to unrelated Nplus/Pplus. is 0.18µm
+logger.info("Executing rule SB.15a")
+sb15a_l1 = poly2.and(sab).separation(nplus.or(pplus), 0.18.um, euclidian).polygons(0.001)
+sb15a_l1.output("SB.15a", "SB.15a : Space from unsalicided Poly2 to unrelated Nplus/Pplus. : 0.18µm")
+sb15a_l1.forget
+
+sb_15b_1 = poly2.interacting(nplus.or(pplus)).and(sab).edges.not(poly2.edges.and(sab)).separation(nplus.or(pplus).edges, 0.32.um, projection).polygons(0.001)
+sb_15b_2 = poly2.interacting(nplus.or(pplus)).and(sab).separation(nplus.or(pplus), 0.32.um, projection).polygons(0.001)
+# Rule SB.15b: Space from unsalicided Poly2 to unrelated Nplus/Pplus along Poly2 line. is 0.32µm
+logger.info("Executing rule SB.15b")
+sb15b_l1 = sb_15b_1.and(sb_15b_2).outside(otp_mk)
+sb15b_l1.output("SB.15b", "SB.15b : Space from unsalicided Poly2 to unrelated Nplus/Pplus along Poly2 line. : 0.32µm")
+sb15b_l1.forget
+
+sb_15b_1.forget
+sb_15b_2.forget
+# Rule SB.16: SAB layer cannot exist on 3.3V and 5V/6V CMOS transistors' Poly and COMP area of the core circuit (Excluding the transistors used for ESD purpose). It can only exist on CMOS transistors marked by LVS_IO, OTP_MK, ESD_MK layers.
+logger.info("Executing rule SB.16")
+sb16_l1 = sab.outside(otp_mk).outside(otp_mk.or(lvs_io).or(esd_mk)).not_outside(ngate.or(pgate.and(nwell)))
+sb16_l1.output("SB.16", "SB.16 : SAB layer cannot exist on 3.3V and 5V/6V CMOS transistors' Poly and COMP area of the core circuit (Excluding the transistors used for ESD purpose). It can only exist on CMOS transistors marked by LVS_IO, OTP_MK, ESD_MK layers.")
+sb16_l1.forget
+
+
+end #FEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/via1.drc b/rules/klayout/drc/rule_decks/via1.drc
new file mode 100644
index 0000000..2f47e55
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/via1.drc
@@ -0,0 +1,313 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (VIA1) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal1 = polygons(34 , 0 )
+via1 = polygons(35 , 0 )
+metal2 = polygons(36 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------VIA1----------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule V1.1: Min/max Via1 size . is 0.26µm
+logger.info("Executing rule V1.1")
+v11_l1 = via1.edges.without_length(0.26.um).extended(0, 0, 0.001, 0.001)
+v11_l1.output("V1.1", "V1.1 : Min/max Via1 size . : 0.26µm")
+v11_l1.forget
+
+# Rule V1.2a: min. via1 spacing is 0.26µm
+logger.info("Executing rule V1.2a")
+v12a_l1 = via1.space(0.26.um, euclidian).polygons(0.001)
+v12a_l1.output("V1.2a", "V1.2a : min. via1 spacing : 0.26µm")
+v12a_l1.forget
+
+merged_via1 = via1.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.82.um , nil).extents.inside(metal1)
+via1_mask = merged_via1.size(1).not(via1).with_holes(16, nil)
+selected_via1 = via1.interacting(via1_mask)
+# Rule V1.2b: Via1 Space in 4x4 or larger via1 array is 0.36µm
+logger.info("Executing rule V1.2b")
+v12b_l1 = selected_via1.space(0.36.um, euclidian).polygons(0.001)
+v12b_l1.output("V1.2b", "V1.2b : Via1 Space in 4x4 or larger via1 array : 0.36µm")
+v12b_l1.forget
+
+merged_via1.forget
+via1_mask.forget
+selected_via1.forget
+# Rule V1.3a: metal-1 overlap of via1.
+logger.info("Executing rule V1.3a")
+v13a_l1 = via1.not(metal1)
+v13a_l1.output("V1.3a", "V1.3a : metal-1 overlap of via1.")
+v13a_l1.forget
+
+# rule V1.3b is not a DRC check
+
+v1p3c_cond = metal1.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v1p3c_eol = metal1.edges.with_length(nil, 0.34.um).interacting(v1p3c_cond.first_edges).interacting(v1p3c_cond.second_edges).not(v1p3c_cond.first_edges).not(v1p3c_cond.second_edges)
+# Rule V1.3c: metal-1 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V1.3c")
+v13c_l1 = v1p3c_eol.enclosing(via1.edges,0.06.um, projection).polygons(0.001)
+v13c_l1.output("V1.3c", "V1.3c : metal-1 (< 0.34um) end-of-line overlap. : 0.06µm")
+v13c_l1.forget
+
+v1p3c_cond.forget
+v1p3c_eol.forget
+v1_3d_1 = via1.edges.interacting(via1.drc(enclosed(metal1, projection) < 0.04.um).edges.centers(0, 0.5))
+v1_3d_2 = via1.edges.interacting(via1.drc(0.04.um <= enclosed(metal1, projection) < 0.06.um).centers(0, 0.5))
+v1_3d_3 = v1_3d_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V1.3d: If metal-1 overlap via1 by < 0.04um on one side, adjacent metal-1 edges overlap. is 0.06µm
+logger.info("Executing rule V1.3d")
+v13d_l1 = v1_3d_2.not_in(v1_3d_1).interacting(v1_3d_1).or(v1_3d_1.interacting(v1_3d_3)).enclosed(metal1.edges, 0.06.um).polygons(0.001)
+v13d_l1.output("V1.3d", "V1.3d : If metal-1 overlap via1 by < 0.04um on one side, adjacent metal-1 edges overlap. : 0.06µm")
+v13d_l1.forget
+
+v1_3d_1.forget
+v1_3d_2.forget
+v1_3d_3.forget
+# rule V1.3e is not a DRC check
+
+# Rule V1.4a: metal-2 overlap of via1.
+logger.info("Executing rule V1.4a")
+v14a_l1 = metal2.enclosing(via1, 0.01.um, euclidian).polygons(0.001).or(via1.not(metal2))
+v14a_l1.output("V1.4a", "V1.4a : metal-2 overlap of via1.")
+v14a_l1.forget
+
+v1p4b_cond = metal2.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v1p4b_eol = metal2.edges.with_length(nil, 0.34.um).interacting(v1p4b_cond.first_edges).interacting(v1p4b_cond.second_edges).not(v1p4b_cond.first_edges).not(v1p4b_cond.second_edges)
+# Rule V1.4b: metal-2 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V1.4b")
+v14b_l1 = v1p4b_eol.enclosing(via1.edges,0.06.um, projection).polygons(0.001)
+v14b_l1.output("V1.4b", "V1.4b : metal-2 (< 0.34um) end-of-line overlap. : 0.06µm")
+v14b_l1.forget
+
+v1p4b_cond.forget
+v1p4b_eol.forget
+v1_4c_1 = via1.edges.interacting(via1.drc(enclosed(metal2, projection) < 0.04.um).edges.centers(0, 0.5))
+v1_4c_2 = via1.edges.interacting(via1.drc(0.04.um <= enclosed(metal2, projection) < 0.06.um).centers(0, 0.5))
+v1_4c_3 = v1_4c_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V1.4c: If metal-2 overlap via1 by < 0.04um on one side, adjacent metal-2 edges overlap. is 0.06µm
+logger.info("Executing rule V1.4c")
+v14c_l1 = v1_4c_2.not_in(v1_4c_1).interacting(v1_4c_1).or(v1_4c_1.interacting(v1_4c_3)).enclosed(metal2.edges, 0.06.um).polygons(0.001)
+v14c_l1.output("V1.4c", "V1.4c : If metal-2 overlap via1 by < 0.04um on one side, adjacent metal-2 edges overlap. : 0.06µm")
+v14c_l1.forget
+
+v1_4c_1.forget
+v1_4c_2.forget
+v1_4c_3.forget
+# rule V1.4d is not a DRC check
+
+# rule V1.5 is not a DRC check
+
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/via2.drc b/rules/klayout/drc/rule_decks/via2.drc
new file mode 100644
index 0000000..1428f15
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/via2.drc
@@ -0,0 +1,313 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (VIA2) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal2 = polygons(36 , 0 )
+via2 = polygons(38 , 0 )
+metal3 = polygons(42 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------VIA2----------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule V2.1: Min/max Via2 size . is 0.26µm
+logger.info("Executing rule V2.1")
+v21_l1 = via2.edges.without_length(0.26.um).extended(0, 0, 0.001, 0.001)
+v21_l1.output("V2.1", "V2.1 : Min/max Via2 size . : 0.26µm")
+v21_l1.forget
+
+# Rule V2.2a: min. via2 spacing is 0.26µm
+logger.info("Executing rule V2.2a")
+v22a_l1 = via2.space(0.26.um, euclidian).polygons(0.001)
+v22a_l1.output("V2.2a", "V2.2a : min. via2 spacing : 0.26µm")
+v22a_l1.forget
+
+merged_via2 = via2.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.82.um , nil).extents.inside(metal2)
+via2_mask = merged_via2.size(1).not(via2).with_holes(16, nil)
+selected_via2 = via2.interacting(via2_mask)
+# Rule V2.2b: Via2 Space in 4x4 or larger via2 array is 0.36µm
+logger.info("Executing rule V2.2b")
+v22b_l1 = selected_via2.space(0.36.um, euclidian).polygons(0.001)
+v22b_l1.output("V2.2b", "V2.2b : Via2 Space in 4x4 or larger via2 array : 0.36µm")
+v22b_l1.forget
+
+merged_via2.forget
+via2_mask.forget
+selected_via2.forget
+# rule V2.3a is not a DRC check
+
+# Rule V2.3b: metal2 overlap of via2.
+logger.info("Executing rule V2.3b")
+v23b_l1 = metal2.enclosing(via2, 0.01.um, euclidian).polygons(0.001).or(via2.not(metal2))
+v23b_l1.output("V2.3b", "V2.3b : metal2 overlap of via2.")
+v23b_l1.forget
+
+v2p3c_cond = metal2.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v2p3c_eol = metal2.edges.with_length(nil, 0.34.um).interacting(v2p3c_cond.first_edges).interacting(v2p3c_cond.second_edges).not(v2p3c_cond.first_edges).not(v2p3c_cond.second_edges)
+# Rule V2.3c: metal2 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V2.3c")
+v23c_l1 = v2p3c_eol.enclosing(via2.edges,0.06.um, projection).polygons(0.001)
+v23c_l1.output("V2.3c", "V2.3c : metal2 (< 0.34um) end-of-line overlap. : 0.06µm")
+v23c_l1.forget
+
+v2p3c_cond.forget
+v2p3c_eol.forget
+v2_3d_1 = via2.edges.interacting(via2.drc(enclosed(metal2, projection) < 0.04.um).edges.centers(0, 0.5))
+v2_3d_2 = via2.edges.interacting(via2.drc(0.04.um <= enclosed(metal2, projection) < 0.06.um).centers(0, 0.5))
+v2_3d_3 = v2_3d_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V2.3d: If metal2 overlap via2 by < 0.04um on one side, adjacent metal2 edges overlap. is 0.06µm
+logger.info("Executing rule V2.3d")
+v23d_l1 = v2_3d_2.not_in(v2_3d_1).interacting(v2_3d_1).or(v2_3d_1.interacting(v2_3d_3)).enclosed(metal2.edges, 0.06.um).polygons(0.001)
+v23d_l1.output("V2.3d", "V2.3d : If metal2 overlap via2 by < 0.04um on one side, adjacent metal2 edges overlap. : 0.06µm")
+v23d_l1.forget
+
+v2_3d_1.forget
+v2_3d_2.forget
+v2_3d_3.forget
+# rule V2.3e is not a DRC check
+
+# Rule V2.4a: metal3 overlap of via2.
+logger.info("Executing rule V2.4a")
+v24a_l1 = metal3.enclosing(via2, 0.01.um, euclidian).polygons(0.001).or(via2.not(metal3))
+v24a_l1.output("V2.4a", "V2.4a : metal3 overlap of via2.")
+v24a_l1.forget
+
+v2p4b_cond = metal3.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v2p4b_eol = metal3.edges.with_length(nil, 0.34.um).interacting(v2p4b_cond.first_edges).interacting(v2p4b_cond.second_edges).not(v2p4b_cond.first_edges).not(v2p4b_cond.second_edges)
+# Rule V2.4b: metal3 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V2.4b")
+v24b_l1 = v2p4b_eol.enclosing(via2.edges,0.06.um, projection).polygons(0.001)
+v24b_l1.output("V2.4b", "V2.4b : metal3 (< 0.34um) end-of-line overlap. : 0.06µm")
+v24b_l1.forget
+
+v2p4b_cond.forget
+v2p4b_eol.forget
+v2_4c_1 = via2.edges.interacting(via2.drc(enclosed(metal3, projection) < 0.04.um).edges.centers(0, 0.5))
+v2_4c_2 = via2.edges.interacting(via2.drc(0.04.um <= enclosed(metal3, projection) < 0.06.um).centers(0, 0.5))
+v2_4c_3 = v2_4c_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V2.4c: If metal3 overlap via2 by < 0.04um on one side, adjacent metal3 edges overlap. is 0.06µm
+logger.info("Executing rule V2.4c")
+v24c_l1 = v2_4c_2.not_in(v2_4c_1).interacting(v2_4c_1).or(v2_4c_1.interacting(v2_4c_3)).enclosed(metal3.edges, 0.06.um).polygons(0.001)
+v24c_l1.output("V2.4c", "V2.4c : If metal3 overlap via2 by < 0.04um on one side, adjacent metal3 edges overlap. : 0.06µm")
+v24c_l1.forget
+
+v2_4c_1.forget
+v2_4c_2.forget
+v2_4c_3.forget
+# rule V2.4d is not a DRC check
+
+# rule V2.5 is not a DRC check
+
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/via3.drc b/rules/klayout/drc/rule_decks/via3.drc
new file mode 100644
index 0000000..e394263
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/via3.drc
@@ -0,0 +1,313 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (VIA3) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal3 = polygons(42 , 0 )
+via3 = polygons(40 , 0 )
+metal4 = polygons(46 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------VIA3----------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule V3.1: Min/max Via3 size . is 0.26µm
+logger.info("Executing rule V3.1")
+v31_l1 = via3.edges.without_length(0.26.um).extended(0, 0, 0.001, 0.001)
+v31_l1.output("V3.1", "V3.1 : Min/max Via3 size . : 0.26µm")
+v31_l1.forget
+
+# Rule V3.2a: min. via3 spacing is 0.26µm
+logger.info("Executing rule V3.2a")
+v32a_l1 = via3.space(0.26.um, euclidian).polygons(0.001)
+v32a_l1.output("V3.2a", "V3.2a : min. via3 spacing : 0.26µm")
+v32a_l1.forget
+
+merged_via3 = via3.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.82.um , nil).extents.inside(metal3)
+via3_mask = merged_via3.size(1).not(via3).with_holes(16, nil)
+selected_via3 = via3.interacting(via3_mask)
+# Rule V3.2b: Via3 Space in 4x4 or larger via3 array is 0.36µm
+logger.info("Executing rule V3.2b")
+v32b_l1 = selected_via3.space(0.36.um, euclidian).polygons(0.001)
+v32b_l1.output("V3.2b", "V3.2b : Via3 Space in 4x4 or larger via3 array : 0.36µm")
+v32b_l1.forget
+
+merged_via3.forget
+via3_mask.forget
+selected_via3.forget
+# rule V3.3a is not a DRC check
+
+# Rule V3.3b: metal3 overlap of via3.
+logger.info("Executing rule V3.3b")
+v33b_l1 = metal3.enclosing(via3, 0.01.um, euclidian).polygons(0.001).or(via3.not(metal3))
+v33b_l1.output("V3.3b", "V3.3b : metal3 overlap of via3.")
+v33b_l1.forget
+
+v3p3c_cond = metal3.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v3p3c_eol = metal3.edges.with_length(nil, 0.34.um).interacting(v3p3c_cond.first_edges).interacting(v3p3c_cond.second_edges).not(v3p3c_cond.first_edges).not(v3p3c_cond.second_edges)
+# Rule V3.3c: metal3 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V3.3c")
+v33c_l1 = v3p3c_eol.enclosing(via3.edges,0.06.um, projection).polygons(0.001)
+v33c_l1.output("V3.3c", "V3.3c : metal3 (< 0.34um) end-of-line overlap. : 0.06µm")
+v33c_l1.forget
+
+v3p3c_cond.forget
+v3p3c_eol.forget
+v3_3d_1 = via3.edges.interacting(via3.drc(enclosed(metal3, projection) < 0.04.um).edges.centers(0, 0.5))
+v3_3d_2 = via3.edges.interacting(via3.drc(0.04.um <= enclosed(metal3, projection) < 0.06.um).centers(0, 0.5))
+v3_3d_3 = v3_3d_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V3.3d: If metal3 overlap via3 by < 0.04um on one side, adjacent metal3 edges overlap. is 0.06µm
+logger.info("Executing rule V3.3d")
+v33d_l1 = v3_3d_2.not(v3_3d_1).interacting(v3_3d_1).or(v3_3d_1.interacting(v3_3d_3)).enclosed(metal3.edges, 0.06.um).polygons(0.001)
+v33d_l1.output("V3.3d", "V3.3d : If metal3 overlap via3 by < 0.04um on one side, adjacent metal3 edges overlap. : 0.06µm")
+v33d_l1.forget
+
+v3_3d_1.forget
+v3_3d_2.forget
+v3_3d_3.forget
+# rule V3.3e is not a DRC check
+
+# Rule V3.4a: metal4 overlap of via3.
+logger.info("Executing rule V3.4a")
+v34a_l1 = metal4.enclosing(via3, 0.01.um, euclidian).polygons(0.001).or(via3.not(metal4))
+v34a_l1.output("V3.4a", "V3.4a : metal4 overlap of via3.")
+v34a_l1.forget
+
+v3p4b_cond = metal4.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v3p4b_eol = metal4.edges.with_length(nil, 0.34.um).interacting(v3p4b_cond.first_edges).interacting(v3p4b_cond.second_edges).not(v3p4b_cond.first_edges).not(v3p4b_cond.second_edges)
+# Rule V3.4b: metal4 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V3.4b")
+v34b_l1 = v3p4b_eol.enclosing(via3.edges,0.06.um, projection).polygons(0.001)
+v34b_l1.output("V3.4b", "V3.4b : metal4 (< 0.34um) end-of-line overlap. : 0.06µm")
+v34b_l1.forget
+
+v3p4b_cond.forget
+v3p4b_eol.forget
+v3_4c_1 = via3.edges.interacting(via3.drc(enclosed(metal4, projection) < 0.04.um).edges.centers(0, 0.5))
+v3_4c_2 = via3.edges.interacting(via3.drc(0.04.um <= enclosed(metal4, projection) < 0.06.um).centers(0, 0.5))
+v3_4c_3 = v3_4c_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V3.4c: If metal4 overlap via3 by < 0.04um on one side, adjacent metal4 edges overlap. is 0.06µm
+logger.info("Executing rule V3.4c")
+v34c_l1 = v3_4c_2.not_in(v3_4c_1).interacting(v3_4c_1).or(v3_4c_1.interacting(v3_4c_3)).enclosed(metal4.edges, 0.06.um).polygons(0.001)
+v34c_l1.output("V3.4c", "V3.4c : If metal4 overlap via3 by < 0.04um on one side, adjacent metal4 edges overlap. : 0.06µm")
+v34c_l1.forget
+
+v3_4c_1.forget
+v3_4c_2.forget
+v3_4c_3.forget
+# rule V3.4d is not a DRC check
+
+# rule V3.5 is not a DRC check
+
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/via4.drc b/rules/klayout/drc/rule_decks/via4.drc
new file mode 100644
index 0000000..1b95644
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/via4.drc
@@ -0,0 +1,313 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (VIA4) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal4 = polygons(46 , 0 )
+via4 = polygons(41 , 0 )
+metal5 = polygons(81 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------VIA4----------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule V4.1: Min/max Via4 size . is 0.26µm
+logger.info("Executing rule V4.1")
+v41_l1 = via4.edges.without_length(0.26.um).extended(0, 0, 0.001, 0.001)
+v41_l1.output("V4.1", "V4.1 : Min/max Via4 size . : 0.26µm")
+v41_l1.forget
+
+# Rule V4.2a: min. via4 spacing is 0.26µm
+logger.info("Executing rule V4.2a")
+v42a_l1 = via4.space(0.26.um, euclidian).polygons(0.001)
+v42a_l1.output("V4.2a", "V4.2a : min. via4 spacing : 0.26µm")
+v42a_l1.forget
+
+merged_via4 = via4.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.82.um , nil).extents.inside(metal4)
+via4_mask = merged_via4.size(1).not(via4).with_holes(16, nil)
+selected_via4 = via4.interacting(via4_mask)
+# Rule V4.2b: Via4 Space in 4x4 or larger Vian array is 0.36µm
+logger.info("Executing rule V4.2b")
+v42b_l1 = selected_via4.space(0.36.um, euclidian).polygons(0.001)
+v42b_l1.output("V4.2b", "V4.2b : Via4 Space in 4x4 or larger Vian array : 0.36µm")
+v42b_l1.forget
+
+merged_via4.forget
+via4_mask.forget
+selected_via4.forget
+# rule V4.3a is not a DRC check
+
+# Rule V4.3b: metal4 overlap of via4.
+logger.info("Executing rule V4.3b")
+v43b_l1 = metal4.enclosing(via4, 0.01.um, euclidian).polygons(0.001).or(via4.not(metal4))
+v43b_l1.output("V4.3b", "V4.3b : metal4 overlap of via4.")
+v43b_l1.forget
+
+v4p3c_cond = metal4.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v4p3c_eol = metal4.edges.with_length(nil, 0.34.um).interacting(v4p3c_cond.first_edges).interacting(v4p3c_cond.second_edges).not(v4p3c_cond.first_edges).not(v4p3c_cond.second_edges)
+# Rule V4.3c: metal4 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V4.3c")
+v43c_l1 = v4p3c_eol.enclosing(via4.edges,0.06.um, projection).polygons(0.001)
+v43c_l1.output("V4.3c", "V4.3c : metal4 (< 0.34um) end-of-line overlap. : 0.06µm")
+v43c_l1.forget
+
+v4p3c_cond.forget
+v4p3c_eol.forget
+v4_3d_1 = via4.edges.interacting(via4.drc(enclosed(metal4, projection) < 0.04.um).edges.centers(0, 0.5))
+v4_3d_2 = via4.edges.interacting(via4.drc(0.04.um <= enclosed(metal4, projection) < 0.06.um).centers(0, 0.5))
+v4_3d_3 = v4_3d_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V4.3d: If metal4 overlap Vian by < 0.04um on one side, adjacent metal4 edges overlap. is 0.06µm
+logger.info("Executing rule V4.3d")
+v43d_l1 = v4_3d_2.not_in(v4_3d_1).interacting(v4_3d_1).or(v4_3d_1.interacting(v4_3d_3)).enclosed(metal4.edges, 0.06.um).polygons(0.001)
+v43d_l1.output("V4.3d", "V4.3d : If metal4 overlap Vian by < 0.04um on one side, adjacent metal4 edges overlap. : 0.06µm")
+v43d_l1.forget
+
+v4_3d_1.forget
+v4_3d_2.forget
+v4_3d_3.forget
+# rule V4.3e is not a DRC check
+
+# Rule V4.4a: metal5 overlap of via4.
+logger.info("Executing rule V4.4a")
+v44a_l1 = metal5.enclosing(via4, 0.01.um, euclidian).polygons(0.001).or(via4.not(metal5))
+v44a_l1.output("V4.4a", "V4.4a : metal5 overlap of via4.")
+v44a_l1.forget
+
+v4p4b_cond = metal5.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v4p4b_eol = metal5.edges.with_length(nil, 0.34.um).interacting(v4p4b_cond.first_edges).interacting(v4p4b_cond.second_edges).not(v4p4b_cond.first_edges).not(v4p4b_cond.second_edges)
+# Rule V4.4b: metal5 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V4.4b")
+v44b_l1 = v4p4b_eol.enclosing(via4.edges,0.06.um, projection).polygons(0.001)
+v44b_l1.output("V4.4b", "V4.4b : metal5 (< 0.34um) end-of-line overlap. : 0.06µm")
+v44b_l1.forget
+
+v4p4b_cond.forget
+v4p4b_eol.forget
+v4_4c_1 = via4.edges.interacting(via4.drc(enclosed(metal5, projection) < 0.04.um).edges.centers(0, 0.5))
+v4_4c_2 = via4.edges.interacting(via4.drc(0.04.um <= enclosed(metal5, projection) < 0.06.um).centers(0, 0.5))
+v4_4c_3 = v4_4c_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V4.4c: If metal5 overlap via4 by < 0.04um on one side, adjacent metal5 edges overlap. is 0.06µm
+logger.info("Executing rule V4.4c")
+v44c_l1 = v4_4c_2.not_in(v4_4c_1).interacting(v4_4c_1).or(v4_4c_1.interacting(v4_4c_3)).enclosed(metal5.edges, 0.06.um).polygons(0.001)
+v44c_l1.output("V4.4c", "V4.4c : If metal5 overlap via4 by < 0.04um on one side, adjacent metal5 edges overlap. : 0.06µm")
+v44c_l1.forget
+
+v4_4c_1.forget
+v4_4c_2.forget
+v4_4c_3.forget
+# rule V4.4d is not a DRC check
+
+# rule V4.5 is not a DRC check
+
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/via5.drc b/rules/klayout/drc/rule_decks/via5.drc
new file mode 100644
index 0000000..6ea3ec0
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/via5.drc
@@ -0,0 +1,313 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (VIA5) -------------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+metal5 = polygons(81 , 0 )
+via5 = polygons(82 , 0 )
+metaltop = polygons(53 , 0 )
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#----------------------VIA5----------------------
+#================================================
+
+
+if BEOL
+logger.info("BEOL section")
+
+# Rule V5.1: Min/max Via5 size . is 0.26µm
+logger.info("Executing rule V5.1")
+v51_l1 = via5.edges.without_length(0.26.um).extended(0, 0, 0.001, 0.001)
+v51_l1.output("V5.1", "V5.1 : Min/max Via5 size . : 0.26µm")
+v51_l1.forget
+
+# Rule V5.2a: min. via5 spacing is 0.26µm
+logger.info("Executing rule V5.2a")
+v52a_l1 = via5.space(0.26.um, euclidian).polygons(0.001)
+v52a_l1.output("V5.2a", "V5.2a : min. via5 spacing : 0.26µm")
+v52a_l1.forget
+
+merged_via5 = via5.sized(0.18.um).sized(-0.18.um).with_bbox_min(1.82.um , nil).extents.inside(metal5)
+via5_mask = merged_via5.size(1).not(via5).with_holes(16, nil)
+selected_via5 = via5.interacting(via5_mask)
+# Rule V5.2b: Via5 Space in 4x4 or larger via5 array is 0.36µm
+logger.info("Executing rule V5.2b")
+v52b_l1 = selected_via5.space(0.36.um, euclidian).polygons(0.001)
+v52b_l1.output("V5.2b", "V5.2b : Via5 Space in 4x4 or larger via5 array : 0.36µm")
+v52b_l1.forget
+
+merged_via5.forget
+via5_mask.forget
+selected_via5.forget
+# rule V5.3a is not a DRC check
+
+# Rule V5.3b: metal5 overlap of via5.
+logger.info("Executing rule V5.3b")
+v53b_l1 = metal5.enclosing(via5, 0.01.um, euclidian).polygons(0.001).or(via5.not(metal5))
+v53b_l1.output("V5.3b", "V5.3b : metal5 overlap of via5.")
+v53b_l1.forget
+
+v5p3c_cond = metal5.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v5p3c_eol = metal5.edges.with_length(nil, 0.34.um).interacting(v5p3c_cond.first_edges).interacting(v5p3c_cond.second_edges).not(v5p3c_cond.first_edges).not(v5p3c_cond.second_edges)
+# Rule V5.3c: metal5 (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V5.3c")
+v53c_l1 = v5p3c_eol.enclosing(via5.edges,0.06.um, projection).polygons(0.001)
+v53c_l1.output("V5.3c", "V5.3c : metal5 (< 0.34um) end-of-line overlap. : 0.06µm")
+v53c_l1.forget
+
+v5p3c_cond.forget
+v5p3c_eol.forget
+v5_3d_1 = via5.edges.interacting(via5.drc(enclosed(metal5, projection) < 0.04.um).edges.centers(0, 0.5))
+v5_3d_2 = via5.edges.interacting(via5.drc(0.04.um <= enclosed(metal5, projection) < 0.06.um).centers(0, 0.5))
+v5_3d_3 = v5_3d_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V5.3d: If metal5 overlap via5 by < 0.04um on one side, adjacent metal5 edges overlap. is 0.06µm
+logger.info("Executing rule V5.3d")
+v53d_l1 = v5_3d_2.not_in(v5_3d_1).interacting(v5_3d_1).or(v5_3d_1.interacting(v5_3d_3)).enclosed(metal5.edges, 0.06.um).polygons(0.001)
+v53d_l1.output("V5.3d", "V5.3d : If metal5 overlap via5 by < 0.04um on one side, adjacent metal5 edges overlap. : 0.06µm")
+v53d_l1.forget
+
+v5_3d_1.forget
+v5_3d_2.forget
+v5_3d_3.forget
+# rule V5.3e is not a DRC check
+
+# Rule V5.4a: metaltop overlap of via5.
+logger.info("Executing rule V5.4a")
+v54a_l1 = metaltop.enclosing(via5, 0.01.um, euclidian).polygons(0.001).or(via5.not(metaltop))
+v54a_l1.output("V5.4a", "V5.4a : metaltop overlap of via5.")
+v54a_l1.forget
+
+v5p4b_cond = metaltop.drc( width <= 0.34.um).with_length(0.28.um,nil,both)
+v5p4b_eol = metaltop.edges.with_length(nil, 0.34.um).interacting(v5p4b_cond.first_edges).interacting(v5p4b_cond.second_edges).not(v5p4b_cond.first_edges).not(v5p4b_cond.second_edges)
+# Rule V5.4b: metaltop (< 0.34um) end-of-line overlap. is 0.06µm
+logger.info("Executing rule V5.4b")
+v54b_l1 = v5p4b_eol.enclosing(via5.edges,0.06.um, projection).polygons(0.001)
+v54b_l1.output("V5.4b", "V5.4b : metaltop (< 0.34um) end-of-line overlap. : 0.06µm")
+v54b_l1.forget
+
+v5p4b_cond.forget
+v5p4b_eol.forget
+v5_4c_1 = via5.edges.interacting(via5.drc(enclosed(metaltop, projection) < 0.04.um).edges.centers(0, 0.5))
+v5_4c_2 = via5.edges.interacting(via5.drc(0.04.um <= enclosed(metaltop, projection) < 0.06.um).centers(0, 0.5))
+v5_4c_3 = v5_4c_1.extended(0, 0, 0, 0.001, joined).corners(90)
+# Rule V5.4c: If metaltop overlap via5 by < 0.04um on one side, adjacent metaltop edges overlap. is 0.06µm
+logger.info("Executing rule V5.4c")
+v54c_l1 = v5_4c_2.not_in(v5_4c_1).interacting(v5_4c_1).or(v5_4c_1.interacting(v5_4c_3)).enclosed(metaltop.edges, 0.06.um).polygons(0.001)
+v54c_l1.output("V5.4c", "V5.4c : If metaltop overlap via5 by < 0.04um on one side, adjacent metaltop edges overlap. : 0.06µm")
+v54c_l1.forget
+
+v5_4c_1.forget
+v5_4c_2.forget
+v5_4c_3.forget
+# rule V5.4d is not a DRC check
+
+# rule V5.5 is not a DRC check
+
+
+end #BEOL
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/rule_decks/ymtp_mk.drc b/rules/klayout/drc/rule_decks/ymtp_mk.drc
new file mode 100644
index 0000000..8a2614d
--- /dev/null
+++ b/rules/klayout/drc/rule_decks/ymtp_mk.drc
@@ -0,0 +1,316 @@
+# Copyright 2022 GlobalFoundries PDK Authors
+#
+# 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
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#=============================================================================================================================================================
+#----------------------------------------------------------- GF 0.18um MCU DRC RULE DECK (YMTP_MK) -----------------------------------------------------------
+#=============================================================================================================================================================
+
+require 'time'
+require "logger"
+
+exec_start_time = Time.now
+
+logger = Logger.new(STDOUT)
+
+logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
+"
+end
+
+#================================================
+#----------------- FILE SETUP -------------------
+#================================================
+
+# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
+
+logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
+
+if $input
+ if $topcell
+ source($input, $topcell)
+ else
+ source($input)
+ end
+end
+
+logger.info("Loading database to memory is complete.")
+
+if $report
+ logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
+ report("DRC Run Report at", $report)
+else
+ logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
+ report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
+end
+
+if $thr
+ logger.info("Number of threads to use %s" % [$thr])
+ threads($thr)
+else
+ logger.info("Number of threads to use 16")
+ threads(16)
+end
+
+# === TILING MODE ===
+if $run_mode == "tiling"
+ # use a tile size of 1mm - not used in deep mode-
+ # tiles(500.um)
+ # use a tile border of 10 micron:
+ # tile_borders(10.um)
+ tiles(1000)
+ logger.info("Tiling mode is enabled.")
+
+elsif $run_mode == "deep"
+ #=== HIER MODE ===
+ deep
+ logger.info("deep mode is enabled.")
+
+elsif $run_mode == "flat"
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+else
+ #=== FLAT MODE ===
+ flat
+ logger.info("flat mode is enabled.")
+
+end # run_mode
+
+#================================================
+#------------- LAYERS DEFINITIONS ---------------
+#================================================
+
+v5_xtor = polygons(112, 1 )
+dualgate = polygons(55 , 0 )
+poly2 = polygons(30 , 0 )
+nplus = polygons(32 , 0 )
+pplus = polygons(31 , 0 )
+comp = polygons(22 , 0 )
+dnwell = polygons(12 , 0 )
+nwell = polygons(21 , 0 )
+otp_mk = polygons(173, 5 )
+plfuse = polygons(125, 5 )
+ymtp_mk = polygons(86 , 17)
+
+logger.info("Starting deriving base layers.")
+#================================================
+#------------- LAYERS DERIVATIONS ---------------
+#================================================
+
+ncomp = comp & nplus
+pcomp = comp & pplus
+tgate = poly2 & comp
+ngate = nplus & tgate
+pgate = pplus & tgate
+
+#================================================
+#------------------ SWITCHES --------------------
+#================================================
+logger.info("Evaluate switches.")
+
+# FEOL
+if $feol == "false"
+ FEOL = $feol
+ logger.info("FEOL is disabled.")
+else
+ FEOL = "true"
+ logger.info("FEOL is enabled.")
+end # FEOL
+
+# BEOL
+if $beol == "false"
+ BEOL = $beol
+ logger.info("BEOL is disabled.")
+else
+ BEOL = "true"
+ logger.info("BEOL is enabled.")
+end # BEOL
+
+# connectivity rules
+if $conn_drc == "true"
+ CONNECTIVITY_RULES = $conn_drc
+ logger.info("connectivity rules are enabled.")
+else
+ CONNECTIVITY_RULES = false
+ logger.info("connectivity rules are disabled.")
+end # connectivity rules
+
+# METAL_TOP
+if $metal_top
+ METAL_TOP = $metal_top
+else
+ METAL_TOP = "9K"
+end # METAL_TOP
+
+logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
+
+# METAL_LEVEL
+if $metal_level
+ METAL_LEVEL = $metal_level
+else
+ METAL_LEVEL = "6LM"
+end # METAL_LEVEL
+
+logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
+
+# WEDGE
+if $wedge == "false"
+ WEDGE = $wedge
+else
+ WEDGE = "true"
+end # WEDGE
+
+logger.info("Wedge enabled %s" % [WEDGE])
+
+# BALL
+if $ball == "false"
+ BALL = $ball
+else
+ BALL = "true"
+end # BALL
+
+logger.info("Ball enabled %s" % [BALL])
+
+# GOLD
+if $gold == "false"
+ GOLD = $gold
+else
+ GOLD = "true"
+end # GOLD
+
+logger.info("Gold enabled %s" % [GOLD])
+
+if $mim_option
+ MIM_OPTION = $mim_option
+else
+ MIM_OPTION = "Nan"
+end
+
+logger.info("MIM Option selected %s" % [MIM_OPTION])
+
+# OFFGRID
+if $offgrid == "false"
+ OFFGRID = false
+else
+ OFFGRID = true
+end # OFFGRID
+
+logger.info("Offgrid enabled %s" % [OFFGRID])
+
+#================================================
+#--------------------YMTP_MK---------------------
+#================================================
+
+# Rule Y.NW.2b_3.3V: Min. Nwell Space (Outside DNWELL, Inside YMTP_MK) [Different potential]. is 1µm
+logger.info("Executing rule Y.NW.2b_3.3V")
+ynw2b_l1 = nwell.outside(dnwell).inside(ymtp_mk).space(1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ynw2b_l1.output("Y.NW.2b_3.3V", "Y.NW.2b_3.3V : Min. Nwell Space (Outside DNWELL, Inside YMTP_MK) [Different potential]. : 1µm")
+ynw2b_l1.forget
+
+# Rule Y.NW.2b_5V: Min. Nwell Space (Outside DNWELL, Inside YMTP_MK) [Different potential]. is 1µm
+logger.info("Executing rule Y.NW.2b_5V")
+ynw2b_l1 = nwell.outside(dnwell).inside(ymtp_mk).space(1.um, euclidian).polygons(0.001).overlapping(dualgate)
+ynw2b_l1.output("Y.NW.2b_5V", "Y.NW.2b_5V : Min. Nwell Space (Outside DNWELL, Inside YMTP_MK) [Different potential]. : 1µm")
+ynw2b_l1.forget
+
+# rule Y.DF.4d_3.3V is not a DRC check
+
+# rule Y.DF.4d_5V is not a DRC check
+
+# Rule Y.DF.6_5V: Min. COMP extend beyond gate (it also means source/drain overhang) inside YMTP_MK. is 0.15µm
+logger.info("Executing rule Y.DF.6_5V")
+ydf6_l1 = comp.not(otp_mk).inside(ymtp_mk).enclosing(poly2.inside(ymtp_mk), 0.15.um, euclidian).polygons(0.001).overlapping(dualgate)
+ydf6_l1.output("Y.DF.6_5V", "Y.DF.6_5V : Min. COMP extend beyond gate (it also means source/drain overhang) inside YMTP_MK. : 0.15µm")
+ydf6_l1.forget
+
+# Rule Y.DF.16_3.3V: Min. space from (Nwell outside DNWELL) to (unrelated NCOMP outside Nwell and DNWELL) (inside YMTP_MK). is 0.27µm
+logger.info("Executing rule Y.DF.16_3.3V")
+ydf16_l1 = ncomp.outside(nwell).outside(dnwell).separation(nwell.outside(dnwell), 0.27.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ydf16_l1.output("Y.DF.16_3.3V", "Y.DF.16_3.3V : Min. space from (Nwell outside DNWELL) to (unrelated NCOMP outside Nwell and DNWELL) (inside YMTP_MK). : 0.27µm")
+ydf16_l1.forget
+
+# Rule Y.DF.16_5V: Min. space from (Nwell outside DNWELL) to (unrelated NCOMP outside Nwell and DNWELL) (inside YMTP_MK). is 0.23µm
+logger.info("Executing rule Y.DF.16_5V")
+ydf16_l1 = ncomp.outside(nwell).outside(dnwell).separation(nwell.outside(dnwell), 0.23.um, euclidian).polygons(0.001).overlapping(dualgate)
+ydf16_l1.output("Y.DF.16_5V", "Y.DF.16_5V : Min. space from (Nwell outside DNWELL) to (unrelated NCOMP outside Nwell and DNWELL) (inside YMTP_MK). : 0.23µm")
+ydf16_l1.forget
+
+# Rule Y.PL.1_3.3V: Interconnect Width (inside YMTP_MK). is 0.13µm
+logger.info("Executing rule Y.PL.1_3.3V")
+ypl1_l1 = poly2.outside(plfuse).and(ymtp_mk).width(0.13.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ypl1_l1.output("Y.PL.1_3.3V", "Y.PL.1_3.3V : Interconnect Width (inside YMTP_MK). : 0.13µm")
+ypl1_l1.forget
+
+# Rule Y.PL.1_5V: Interconnect Width (inside YMTP_MK). This rule is currently not applicable for 5V.
+logger.info("Executing rule Y.PL.1_5V")
+ypl1_l1 = poly2.outside(plfuse).and(ymtp_mk).overlapping(dualgate)
+ypl1_l1.output("Y.PL.1_5V", "Y.PL.1_5V : Interconnect Width (inside YMTP_MK). This rule is currently not applicable for 5V.")
+ypl1_l1.forget
+
+# Rule Y.PL.2_3.3V: Gate Width (Channel Length) (inside YMTP_MK). is 0.13µm
+logger.info("Executing rule Y.PL.2_3.3V")
+ypl2_l1 = poly2.edges.and(tgate.edges).not(otp_mk).and(ymtp_mk).width(0.13.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ypl2_l1.output("Y.PL.2_3.3V", "Y.PL.2_3.3V : Gate Width (Channel Length) (inside YMTP_MK). : 0.13µm")
+ypl2_l1.forget
+
+# Rule Y.PL.2_5V: Gate Width (Channel Length) (inside YMTP_MK). is 0.47µm
+logger.info("Executing rule Y.PL.2_5V")
+ypl2_l1 = poly2.edges.and(tgate.edges).not(otp_mk).and(ymtp_mk).width(0.47.um, euclidian).polygons(0.001).overlapping(dualgate)
+ypl2_l1.output("Y.PL.2_5V", "Y.PL.2_5V : Gate Width (Channel Length) (inside YMTP_MK). : 0.47µm")
+ypl2_l1.forget
+
+# Rule Y.PL.4_5V: Poly2 extension beyond COMP to form Poly2 end cap (inside YMTP_MK). is 0.16µm
+logger.info("Executing rule Y.PL.4_5V")
+ypl4_l1 = poly2.and(ymtp_mk).enclosing(comp.and(ymtp_mk), 0.16.um, euclidian).polygons(0.001).overlapping(dualgate)
+ypl4_l1.output("Y.PL.4_5V", "Y.PL.4_5V : Poly2 extension beyond COMP to form Poly2 end cap (inside YMTP_MK). : 0.16µm")
+ypl4_l1.forget
+
+# Rule Y.PL.5a_3.3V: Space from field Poly2 to unrelated COMP (inside YMTP_MK). Space from field Poly2 to Guard-ring (inside YMTP_MK). is 0.04µm
+logger.info("Executing rule Y.PL.5a_3.3V")
+ypl5a_l1 = poly2.and(ymtp_mk).separation(comp.and(ymtp_mk), 0.04.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ypl5a_l1.output("Y.PL.5a_3.3V", "Y.PL.5a_3.3V : Space from field Poly2 to unrelated COMP (inside YMTP_MK). Space from field Poly2 to Guard-ring (inside YMTP_MK). : 0.04µm")
+ypl5a_l1.forget
+
+# Rule Y.PL.5a_5V: Space from field Poly2 to unrelated COMP (inside YMTP_MK). Space from field Poly2 to Guard-ring (inside YMTP_MK). is 0.2µm
+logger.info("Executing rule Y.PL.5a_5V")
+ypl5a_l1 = poly2.and(ymtp_mk).separation(comp.and(ymtp_mk), 0.2.um, euclidian).polygons(0.001).overlapping(dualgate)
+ypl5a_l1.output("Y.PL.5a_5V", "Y.PL.5a_5V : Space from field Poly2 to unrelated COMP (inside YMTP_MK). Space from field Poly2 to Guard-ring (inside YMTP_MK). : 0.2µm")
+ypl5a_l1.forget
+
+# Rule Y.PL.5b_3.3V: Space from field Poly2 to related COMP (inside YMTP_MK). is 0.04µm
+logger.info("Executing rule Y.PL.5b_3.3V")
+ypl5b_l1 = poly2.and(ymtp_mk).separation(comp.and(ymtp_mk), 0.04.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
+ypl5b_l1.output("Y.PL.5b_3.3V", "Y.PL.5b_3.3V : Space from field Poly2 to related COMP (inside YMTP_MK). : 0.04µm")
+ypl5b_l1.forget
+
+# Rule Y.PL.5b_5V: Space from field Poly2 to related COMP (inside YMTP_MK). is 0.2µm
+logger.info("Executing rule Y.PL.5b_5V")
+ypl5b_l1 = poly2.and(ymtp_mk).separation(comp.and(ymtp_mk), 0.2.um, euclidian).polygons(0.001).overlapping(dualgate)
+ypl5b_l1.output("Y.PL.5b_5V", "Y.PL.5b_5V : Space from field Poly2 to related COMP (inside YMTP_MK). : 0.2µm")
+ypl5b_l1.forget
+
+# rule Y.PL.6_3.3V is not a DRC check
+
+# rule Y.PL.6_5V is not a DRC check
+
+# rule Y.LU.3_3.3V is not yet implemented
+
+# rule Y.LU.3_5V is not yet implemented
+
+
+exec_end_time = Time.now
+run_time = exec_end_time - exec_start_time
+logger.info("DRC Run time %f seconds" % [run_time])
+
diff --git a/rules/klayout/drc/run_drc_parallel.py b/rules/klayout/drc/run_drc_parallel.py
new file mode 100644
index 0000000..57c0b1d
--- /dev/null
+++ b/rules/klayout/drc/run_drc_parallel.py
@@ -0,0 +1,317 @@
+#########################################################
+
+"""
+Run Globalfoundries 180u DRC.
+
+Usage:
+ run_drc.py (--help| -h)
+ run_drc.py (--path=<file_path>) (--gf180mcu=<combined_options>) [--topcell=<topcell_name>] [--thr=<thr>] [--run_mode=<run_mode>] [--no_feol] [--no_beol] [--connectivity] [--density] [--density_only] [--antenna] [--antenna_only] [--no_offgrid]
+
+Options:
+ --help -h Print this help message.
+ --path=<file_path> The input GDS file path.
+ --gf180mcu=<combined_options> Select combined options of metal_top, mim_option, and metal_level. Allowed values (A, B, C).
+ gf180mcu=A: Select metal_top=30K mim_option=A metal_level=3LM
+ gf180mcu=B: Select metal_top=11K mim_option=B metal_level=4LM
+ gf180mcu=C: Select metal_top=9K mim_option=B metal_level=5LM
+ --topcell=<topcell_name> Topcell name to use.
+ --thr=<thr> The number of threads used in run.
+ --run_mode=<run_mode> Select klayout mode Allowed modes (flat , deep, tiling). [default: flat]
+ --no_feol Turn off FEOL rules from running.
+ --no_beol Turn off BEOL rules from running.
+ --connectivity Turn on connectivity rules.
+ --density Turn on Density rules.
+ --density_only Turn on Density rules only.
+ --antenna Turn on Antenna checks.
+ --antenna_only Turn on Antenna checks only.
+ --no_offgrid Turn off OFFGRID checking rules.
+"""
+
+from docopt import docopt
+import os
+import xml.etree.ElementTree as ET
+import logging
+import subprocess
+import concurrent.futures
+# import logging
+# from multiprocessing import Process, log_to_stderr
+
+def call_simulator(arg):
+ """
+ It runs the simulator with the given rule deck and input file, and saves the output to a database
+ file
+
+ :param rule_deck_path: The path to the rule deck file
+ :param path: The path to the GDS file you want to simulate
+ :param thrCount: number of threads to use
+ """
+ os.system(arg)
+
+def combine_results(path, rule_decks):
+ name_clean_= path.replace(".gds","")
+ path_clean = '/'.join(name_clean_.split("/")[:-1])
+ get_category = False
+ get_item = False
+ categories = []
+ categories_block = []
+ items = []
+
+ for i, rule_deck in enumerate(rule_decks):
+ if i == 0: continue
+ with open(f"{name_clean_}_main_drc_gf{arguments['--gf180mcu']}_{i}.lyrdb", 'r') as f:
+ for line in f:
+ if "<cells>" in line: get_category = False
+ if get_category == True: categories.append(line)
+ if "</tags>" in line: get_category = True
+ if "</items>" in line: get_item = False
+ if get_item == True: items.append(line)
+ if "<items>" in line: get_item = True
+ categories = categories[1:-1]
+ categories_block.append(''.join(categories))
+ categories = []
+
+ with open(f"{name_clean_}_main_drc_gf{arguments['--gf180mcu']}_{0}.lyrdb", "r") as f:
+ contents = f.readlines()
+ contents.insert(9, ''.join(categories_block))
+ contents.insert(-2, ''.join(items))
+
+ with open(f"{name_clean_}_main_drc_gf{arguments['--gf180mcu']}.lyrdb", "w") as f:
+ f.writelines(contents)
+
+ os.system(f"rm -rf {name_clean_}_main_drc_gf{arguments['--gf180mcu']}_*")
+ os.system(f"mkdir {path_clean}/logs")
+ os.system(f"mv *.log {path_clean}/logs")
+
+def get_results(rule_deck,rules,lyrdb, type):
+
+ mytree = ET.parse(f"{lyrdb}_{type}_gf{arguments['--gf180mcu']}.lyrdb")
+ myroot = mytree.getroot()
+
+ violated = []
+
+ for lrule in rules:
+ # Loop on database to get the violations of required rule
+ for z in myroot[7]:
+ if f"'{lrule}'" == f"{z[1].text}":
+ violated.append(lrule)
+ break
+
+ lyrdb_clean = lyrdb.split("/") [-1]
+
+ if len(violated) > 0:
+ logging.error(f"\nTotal # of DRC violations in {rule_deck}.drc is {len(violated)}. Please check {lyrdb_clean}_{type}_gf{arguments['--gf180mcu']}.lyrdb file For more details")
+ logging.info("Klayout GDS DRC Not Clean")
+ logging.info(f"Violated rules are : {violated}\n")
+ else:
+ logging.info(f"\nCongratulations !!. No DRC Violations found in {lyrdb_clean} for {rule_deck}.drc rule deck with switch gf{arguments['--gf180mcu']}")
+ logging.info("Klayout GDS DRC Clean\n")
+
+def get_top_cell_names(gds_path):
+ # klayout -b -r script.rb -rd infile=./layouts/caravel.gds.gz
+
+ pdk_root = os.environ['PDK_ROOT']
+ pdk = os.environ['PDK']
+
+ top_cell_names = list()
+ proc = subprocess.Popen(['klayout','-b', '-r', f"{pdk_root}/{pdk}/utils/get_top_cell_names.rb", "-rd", "infile={}".format(gds_path)], stdout=subprocess.PIPE)
+ while True:
+ line = proc.stdout.readline()
+ if not line:
+ break
+ top_cell_names.append(line.decode().strip())
+
+ return top_cell_names
+
+def clean_gds_from_many_top_cells(gds_path, topcell):
+ # klayout -b -r keep_single_top_cell.rb -rd infile=./layouts/caravel.gds.gz -rd topcell=chip_io -rd outfile=test.gds.gz
+
+ pdk_root = os.environ['PDK_ROOT']
+ pdk = os.environ['PDK']
+
+ basename = os.path.basename(gds_path)
+ dirname = os.path.dirname(gds_path)
+ main_file_name = basename.split(".")[0]
+ output_file_path = os.path.join(dirname, "{}_single_top.gds.gz".format(main_file_name))
+
+ proc = subprocess.Popen(['klayout','-b', '-r', f"{pdk_root}/{pdk}/utils/keep_single_top_cell.rb", "-rd", "infile={}".format(gds_path), "-rd", "topcell={}".format(topcell), "-rd", "outfile={}".format(output_file_path)], stdout=subprocess.PIPE)
+
+ while True:
+ line = proc.stdout.readline()
+ if not line:
+ break
+ print(line.strip())
+ return output_file_path
+
+def main():
+
+ # check gds file existance
+ if os.path.exists(arguments["--path"]):
+ pass
+ else:
+ logging.error("The input GDS file path doesn't exist, please recheck.")
+ exit()
+
+ # Env. variables
+ pdk_root = os.environ['PDK_ROOT']
+ pdk = os.environ['PDK']
+
+ # ======= Checking Klayout version =======
+ klayout_v_ = os.popen("klayout -v").read()
+ klayout_v_ = klayout_v_.split("\n")[0]
+ klayout_v = int (klayout_v_.split(".") [-1])
+
+ logging.info(f"Your Klayout version is: {klayout_v_}" )
+
+ if klayout_v < 8:
+ logging.info(f"Prerequisites at a minimum: KLayout 0.27.8")
+ logging.error("Using this klayout version has not been assesed in this development. Limits are unknown")
+
+ # Switches used in run
+ switches = ''
+ runs = []
+
+ if arguments["--run_mode"] in ["flat" , "deep", "tiling"]:
+ switches = switches + f'-rd run_mode={arguments["--run_mode"]} '
+ else:
+ logging.error("Allowed klayout modes are (flat , deep , tiling) only")
+ exit()
+
+ if arguments["--gf180mcu"] == "A": switches = switches + f'-rd metal_top=30K -rd mim_option=A -rd metal_level=3LM '
+ elif arguments["--gf180mcu"] == "B": switches = switches + f'-rd metal_top=11K -rd mim_option=B -rd metal_level=4LM '
+ elif arguments["--gf180mcu"] == "C": switches = switches + f'-rd metal_top=9K -rd mim_option=B -rd metal_level=5LM '
+ else:
+ logging.error("gf180mcu switch allowed values are (A , B, C) only")
+ exit()
+
+ if arguments["--no_feol"]: switches = switches + '-rd feol=false '
+ else: switches = switches + '-rd feol=true '
+
+ if arguments["--no_beol"]: switches = switches + '-rd beol=false '
+ else: switches = switches + '-rd beol=true '
+
+ if arguments["--no_offgrid"]: switches = switches + '-rd offgrid=false '
+ else: switches = switches + '-rd offgrid=true '
+
+ if arguments["--connectivity"]: switches = switches + '-rd conn_drc=true '
+ else: switches = switches + '-rd conn_drc=false '
+
+ if arguments["--density"]: switches = switches + '-rd density=true '
+ else: switches = switches + '-rd density=false '
+
+ # Generate databases
+ if arguments["--path"]:
+ path = arguments["--path"]
+ topcell_name = arguments["--topcell"]
+
+ if ".gds" in path:
+ name_clean_= path.replace(".gds","")
+ name_clean = name_clean_.split("/")[-1]
+
+ tc_list = get_top_cell_names(path)
+ if len(tc_list) < 2:
+ topcell_name = tc_list[0]
+ else:
+ print("## File has multiple topcell names")
+ # print("## File has multiple topcell names, must provide topcellname.")
+ # if topcell_name is None:
+ print("## Will have to stop.")
+ exit()
+
+ # if not topcell_name is None:
+ switches = switches + f'-rd topcell={topcell_name}'
+
+ # Removing old db
+ os.system(f"rm -rf {name_clean_}_main_drc_gf{arguments['--gf180mcu']}.lyrdb {name_clean_}_antenna_gf{arguments['--gf180mcu']}.lyrdb {name_clean_}_density_gf{arguments['--gf180mcu']}.lyrdb")
+
+ # Running DRC using klayout
+ if (arguments["--antenna_only"]) and not (arguments["--density_only"]):
+ logging.info(f"Running Global Foundries 180nm MCU antenna checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_antenna.drc -rd input={path} -rd report={name_clean}_antenna_gf{arguments['--gf180mcu']}_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+
+ elif (arguments["--density_only"]) and not (arguments["--antenna_only"]):
+ logging.info(f"Running Global Foundries 180nm MCU density checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_density.drc -rd input={path} -rd report={name_clean}_density_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+
+ elif arguments["--antenna_only"] and arguments["--density_only"]:
+ logging.info(f"Running Global Foundries 180nm MCU antenna checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_antenna.drc -rd input={path} -rd report={name_clean}_antenna_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+
+ logging.info(f"Running Global Foundries 180nm MCU density checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_density.drc -rd input={path} -rd report={name_clean}_density_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+
+ else:
+ logging.info(f"Running main Global Foundries 180nm MCU runset on design {name_clean} on cell {topcell_name}:")
+ rule_decks = os.listdir(f"{os.environ['PDK_ROOT']}/{os.environ['PDK']}/rule_decks/")
+ for i, rule_deck in enumerate(rule_decks):
+ #os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu.drc -rd input={path} -rd report={name_clean}_main_drc_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+ arg = f"klayout -b -r $PDK_ROOT/$PDK/rule_decks/{rule_deck} -rd input={path} -rd report={name_clean}_main_drc_gf{arguments['--gf180mcu']}_{i}.lyrdb -rd thr={thrCount} {switches} | tee {rule_deck}.log"
+ runs.append(arg)
+
+ with concurrent.futures.ProcessPoolExecutor(max_workers=thrCount) as executor:
+ for run in runs:
+ executor.submit(call_simulator, run)
+
+ # log_to_stderr(logging.DEBUG)
+
+ # # Start the process.
+ # for run in runs:
+ # process = Process(target=call_simulator, args=(f"{run}",))
+ # process.start()
+ # process.join()
+
+ combine_results(path, rule_decks)
+
+ if arguments["--antenna"]:
+ logging.info(f"Running Global Foundries 180nm MCU antenna checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_antenna.drc -rd input={path} -rd report={name_clean}_antenna_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+ if arguments["--density"]:
+ logging.info(f"Running Global Foundries 180nm MCU density checks on design {name_clean} on cell {topcell_name}:")
+ os.system(f"klayout -b -r $PDK_ROOT/$PDK/gf180mcu_density.drc -rd input={path} -rd report={name_clean}_density_gf{arguments['--gf180mcu']}.lyrdb -rd thr={thrCount} {switches}")
+ else:
+ logging.error("Script only support gds files, please select one")
+ exit()
+ else:
+ logging.error("No provided gds file, please add one")
+ exit()
+
+ # ======================== Reporting results ========================
+ rule_deck_path = [f"{pdk_root}/{pdk}/gf180mcu.drc" , f"{pdk_root}/{pdk}/gf180mcu_antenna.drc" , f"{pdk_root}/{pdk}/gf180mcu_density.drc"]
+
+ # Get rules from rule deck
+ rules = []
+
+ # Get rules from rule deck
+ for runset in rule_deck_path:
+ with open(runset, 'r') as f:
+ for line in f:
+ if ".output" in line:
+ line_list = line.split('"')
+ if line_list[1] in rules:
+ pass
+ else:
+ rules.append(line_list[1])
+
+ # Get results
+ lyrdbs = [ "main_drc" , "antenna" , "density" ]
+ runsets = [ "gf180mcu" , "gf180mcu_antenna" , "gf180mcu_density"]
+ for i,lyrdb in enumerate(lyrdbs):
+ if os.path.exists(f"{name_clean_}_{lyrdb}_gf{arguments['--gf180mcu']}.lyrdb"):
+ get_results(runsets[i],rules,name_clean_, lyrdb)
+
+# ================================================================
+# -------------------------- MAIN --------------------------------
+# ================================================================
+
+if __name__ == "__main__":
+
+ # logs format
+ logging.basicConfig(level=logging.DEBUG, format=f"%(asctime)s | %(levelname)-7s | %(message)s", datefmt='%d-%b-%Y %H:%M:%S')
+
+ # arguments
+ arguments = docopt(__doc__, version='RUN DRC: 0.1')
+
+ # No. of threads
+ thrCount = os.cpu_count()*2 if arguments["--thr"] == None else int(arguments["--thr"])
+
+ # Calling main function
+ main()
\ No newline at end of file
diff --git a/rules/klayout/lvs/gf180mcu.lvs b/rules/klayout/lvs/gf180mcu.lvs
index d542bfa..9a7c606 100644
--- a/rules/klayout/lvs/gf180mcu.lvs
+++ b/rules/klayout/lvs/gf180mcu.lvs
@@ -153,6 +153,8 @@
def parse_element(s, element)
if element == "C"
super(s + " C=2e-16", element)
+ elsif element == "R"
+ super(s + " R=0", element)
else
super
end
diff --git a/rules/klayout/lvs/testing/run_regression.py b/rules/klayout/lvs/testing/run_regression.py
index 6719492..55b447b 100644
--- a/rules/klayout/lvs/testing/run_regression.py
+++ b/rules/klayout/lvs/testing/run_regression.py
@@ -58,9 +58,12 @@
layout = file[0]
# Get switches
- switches = ''
+ if layout == "sample_ggnmos_6p0_sab":
+ switches = ' -rd lvs_sub=sub!'
+ else:
+ switches = ' -rd lvs_sub=vdd!'
if len(file) > 1:
- switches = file[1]
+ switches = file[1] + switches
# Check if file is mosfet or esd
if "sample" in layout:
@@ -83,7 +86,7 @@
with open(f'testcases/{layout}_generated.cdl', 'w') as file:
file.write(spice_netlist)
- result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input=testcases/{layout}.gds -rd report={layout}.lvsdb -rd schematic={layout}_generated.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches} -rd lvs_sub='vdd!'").read()
+ result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input=testcases/{layout}.gds -rd report={layout}.lvsdb -rd schematic={layout}_generated.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches}").read()
# moving all reports to run dir
out_dir = arguments["--run_dir"]
@@ -100,7 +103,7 @@
for file in man_testing:
file_clean = file.split("/")[-1].replace(".gds","")
if layout == file_clean:
- result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input={file} -rd report={layout}.lvsdb -rd schematic={layout}.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches} -rd lvs_sub='vdd!'").read()
+ result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input={file} -rd report={layout}.lvsdb -rd schematic={layout}.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches}").read()
dir_clean = file.replace(".gds","")
os.system(f"mv -f {dir_clean}.lvsdb {dir_clean}_extracted.cir {out_dir}/LVS_{device_dir}/")
@@ -157,8 +160,9 @@
moscap_files = [['pmoscap_3p3_b'], ['nmoscap_3p3_b'], ['nmoscap_3p3'], ['nmoscap_6p0_b'], ['pmoscap_6p0_dw'], ['nmoscap_6p0'], ['pmoscap_3p3_dw'], ['pmoscap_6p0'], ['nmoscap_3p3_dw'], ['pmoscap_6p0_b'], ['pmoscap_3p3'], ['nmoscap_6p0_dw']]
# ESD (SAB MOSFET)
- esd_files = [['sample_pmos_5p0_sab'], ['sample_nmos_5p0_sab'], ['sample_pmos_3p3_dw_sab'], ['sample_pmos_3p3_sab'], ['sample_pmos_5p0_dw_sab'], ['sample_nmos_6p0_sab'], ['sample_pmos_6p0_dw_sab'], ['sample_nmos_6p0_dw_sab'], ['sample_nmos_3p3_dw_sab'], ['sample_nmos_5p0_dw_sab'], ['sample_nmos_3p3_sab'], ['sample_pmos_6p0_sab']]
-
+ esd_files = [['sample_pmos_5p0_sab'], ['sample_nmos_5p0_sab'], ['sample_pmos_3p3_dw_sab'], ['sample_pmos_3p3_sab'], ['sample_pmos_5p0_dw_sab'], ['sample_nmos_6p0_sab'], ['sample_pmos_6p0_dw_sab'], ['sample_nmos_6p0_dw_sab'], ['sample_nmos_3p3_dw_sab'], ['sample_nmos_5p0_dw_sab'], ['sample_nmos_3p3_sab'], ['sample_pmos_6p0_sab'],
+ ['sample_ggnmos_3p3_sab'], ['sample_ggnmos_6p0_dw_sab'], ['sample_ggnmos_6p0_sab'], ['sample_ggnmos_5p0_sab'], ['sample_ggnmos_5p0_dw_sab'], ['sample_ggnmos_3p3_dw_sab'], ['sample_gppmos_3p3_dw_sab'], ['sample_gppmos_6p0_sab'], ['sample_gppmos_5p0_dw_sab'], ['sample_gppmos_3p3_sab'], ['sample_gppmos_6p0_dw_sab'], ['sample_gppmos_5p0_sab']
+]
# eFuse
efuse_files = [['efuse']]
diff --git a/rules/klayout/lvs/testing/run_sc_regression.py b/rules/klayout/lvs/testing/run_sc_regression.py
index 6a89176..5bbb683 100644
--- a/rules/klayout/lvs/testing/run_sc_regression.py
+++ b/rules/klayout/lvs/testing/run_sc_regression.py
@@ -92,12 +92,12 @@
# os.system(f"mv -f sc_testcases/{sc_input}.lvsdb sc_testcases/*/{cdl_input_clean}_extracted.cir sc_testcases/*/{cdl_input_clean}_modified.cdl {out_dir}/{sc_input_clean}/")
if "INFO : Congratulations! Netlists match." in result:
- logging.info(f"Extraction of {sc_input_clean} is passed")
+ logging.info("Extraction of {:<25s} is passed".format(sc_input_clean))
with open (f"{dir}_testcases/{dir}_report.csv","a+") as rep:
rep.write(f"{sc_input_clean},passed\n")
else:
- logging.info(f"Extraction of {sc_input_clean} is failed")
+ logging.info("Extraction of {:<25s} is failed".format(sc_input_clean))
with open (f"{dir}_testcases/{dir}_report.csv","a+") as rep:
rep.write(f"{sc_input_clean},failed\n")
@@ -135,7 +135,7 @@
end''')
os.system(f"klayout -b -r sc_testcases/split_gds.rb -rd input={cell}.gds")
os.system(f"rm -rf sc_testcases/split_gds.rb")
-
+
# Create cdl splitter script
cdl = cell.split("/")[-1]
os.makedirs(f"sc_testcases/sc_split/sc_netlists/",exist_ok=False)
@@ -165,7 +165,7 @@
else:
cell_clean = cell.replace("ip_testcases/","")
executor.submit(lvs_check, cell_clean)
-
+
# Running LVS on SC
else:
sc_list = glob.glob("sc_testcases/sc_split/*")
@@ -173,6 +173,44 @@
sc_clean = sc.split('.gds')[0].split ('sc_testcases/')[-1]
executor.submit(lvs_check, sc_clean)
+ if os.path.isfile("sc_testcases/sc_report.csv"):
+ df = pd.read_csv("sc_testcases/sc_report.csv")
+ df.columns = ["CELL NAME","RESULT"]
+ df.to_csv("sc_testcases/sc_report.csv", index = False)
+ df = pd.read_csv("sc_testcases/sc_report.csv")
+ pass_count = df["RESULT"].str.count("passed").sum()
+ fail_count = df["RESULT"].str.count("failed").sum()
+
+ logging.info("\n==================================")
+ logging.info(f"NO. OF PASSED SC CELLS : {pass_count}")
+ logging.info(f"NO. OF FAILED SC CELLS : {fail_count}")
+ logging.info("==================================\n")
+
+ # Move split files into run dir
+ shutil.move("sc_testcases/sc_report.csv", out_dir)
+ shutil.move("sc_testcases/sc_split/", out_dir)
+
+ elif os.path.isfile("ip_testcases/ip_report.csv"):
+ df = pd.read_csv("ip_testcases/ip_report.csv")
+ df.columns = ["CELL NAME","RESULT"]
+ df.to_csv("ip_testcases/ip_report.csv", index = False)
+ df = pd.read_csv("ip_testcases/ip_report.csv")
+ pass_count = df["RESULT"].str.count("passed").sum()
+ fail_count = df["RESULT"].str.count("failed").sum()
+
+ logging.info("\n==================================")
+ logging.info(f"NO. OF PASSED IP CELLS : {pass_count}")
+ logging.info(f"NO. OF FAILED IP CELLS : {fail_count}")
+ logging.info("==================================\n")
+
+ # Move files into run dir
+ shutil.move("ip_testcases/ip_report.csv", out_dir)
+
+ else:
+ logging.info("\n==================================")
+ logging.info("Regression Test is failed")
+ logging.info("==================================\n")
+
if __name__ == "__main__":
@@ -196,42 +234,3 @@
# Calling main function
main()
-
- time.sleep(10)
-
- if os.path.isfile("sc_testcases/sc_report.csv"):
- df = pd.read_csv("sc_testcases/sc_report.csv")
- df.columns = ["CELL NAME","RESULT"]
- df.to_csv("sc_testcases/sc_report.csv", index = False)
- df = pd.read_csv("sc_testcases/sc_report.csv")
- pass_count = df["RESULT"].str.count("passed").sum()
- fail_count = df["RESULT"].str.count("failed").sum()
-
- logging.info("\n==================================")
- logging.info(f"NO. OF PASSED SC CELLS : {pass_count}")
- logging.info(f"NO. OF FAILED SC CELLS : {fail_count}")
- logging.info("==================================\n")
-
- # Move split files into run dir
- os.system (f"mv -f sc_testcases/sc_report.csv sc_testcases/sc_split/ {out_dir}")
-
- elif os.path.isfile("ip_testcases/ip_report.csv"):
- df = pd.read_csv("ip_testcases/ip_report.csv")
- df.columns = ["CELL NAME","RESULT"]
- df.to_csv("ip_testcases/ip_report.csv", index = False)
- df = pd.read_csv("ip_testcases/ip_report.csv")
- pass_count = df["RESULT"].str.count("passed").sum()
- fail_count = df["RESULT"].str.count("failed").sum()
-
- logging.info("\n==================================")
- logging.info(f"NO. OF PASSED IP CELLS : {pass_count}")
- logging.info(f"NO. OF FAILED IP CELLS : {fail_count}")
- logging.info("==================================\n")
-
- # Move split files into run dir
- os.system (f"mv -f ip_testcases/ip_report.csv {out_dir}")
-
- else:
- logging.info("\n==================================")
- logging.info("Regression Test is failed")
- logging.info("==================================\n")
diff --git a/rules/klayout/requirements.test.txt b/rules/klayout/requirements.test.txt
index 3f6804c..39af1e5 100644
--- a/rules/klayout/requirements.test.txt
+++ b/rules/klayout/requirements.test.txt
@@ -1,4 +1,4 @@
docopt==0.6.2
pandas==1.3.4
sympy==1.9
-numpy==1.20.3
+numpy==1.22.0