Skip to content

Commit ef693e3

Browse files
committed
Add Makefile
1 parent 4548188 commit ef693e3

File tree

3 files changed

+261
-62
lines changed

3 files changed

+261
-62
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Device Testing Directory
2+
3+
This directory contains setup files, templates, scripts, and reports for device-level verification of the IHP SG13 PDK.
4+
It is organized by device type (MOS, HBT, PNP) and includes a custom test runner (`models_verifier`) and Makefile.
5+
```
6+
📁devices
7+
┣ 📜makefile Make targets to run device tests (pytest or models_verifier).
8+
┣ 📁mos Directory for MOSFET device testing (NMOS/PMOS, LV/HV).
9+
┃ ┣ 📜cap_template.spice.j2 Template for MOS capacitance simulation.
10+
┃ ┣ 📜dc_template.spice.j2 Template for MOS DC sweep simulation.
11+
┃ ┣ 📁nmos_lv Low-voltage NMOS device configs and results.
12+
┃ ┃ ┣ 📜sg13_lv_nmos.yaml Config file for LV NMOS tests.
13+
┃ ┣ 📁nmos_hv High-voltage NMOS device configs.
14+
┃ ┃ ┗ 📜sg13_hv_nmos.yaml Config file for HV NMOS tests.
15+
┃ ┣ 📁pmos_lv Low-voltage PMOS device configs.
16+
┃ ┃ ┗ 📜sg13_lv_pmos.yaml Config file for LV PMOS tests.
17+
┃ ┗ 📁pmos_hv High-voltage PMOS device configs.
18+
┃ ┗ 📜sg13_hv_pmos.yaml Config file for HV PMOS tests.
19+
┣ 📁hbt Directory for HBT (NPN) device testing.
20+
┃ ┣ 📜hbt_dc.spice.j2 Template for HBT DC sweep simulation.
21+
┃ ┣ 📁npn13g2 NPN13G2 variant device configs and results.
22+
┃ ┃ ┣ 📜npn13g2.yaml Config file for NPN13G2.
23+
┃ ┣ 📁npn13g2l NPN13G2L variant configs and results (same structure).
24+
┃ ┗ 📁npn13g2v NPN13G2V variant configs and results (same structure).
25+
┣ 📁pnp_mpa Directory for PNP MPA device testing.
26+
┃ ┣ 📜pnpmpa.yaml Config file for PNP MPA device.
27+
┃ ┣ 📜pnpMPA_dc.spice.j2 Template for PNP DC sweep.
28+
┣ 📁models_verifier Python package for running simulations and verification.
29+
┃ ┣ 📜models_verifier.py CLI entry point for running verification with a config.
30+
┃ ┣ 📜mdm_aggregator.py Combines measurement data into dataframes.
31+
┃ ┣ 📜mdm_parser.py Parser for raw MDM measurement files.
32+
┃ ┣ 📜mdm_parser_utils.py Helper utilities for parsing MDM files.
33+
┃ ┣ 📜mdm_parser_const.py Constants for parsing and verification.
34+
┃ ┣ 📁dc_runner Module for DC sweep simulation.
35+
┃ ┃ ┣ 📜dc_sweep_runner.py Orchestrates ngspice DC runs.
36+
┃ ┃ ┗ 📜helper.py Helper functions for simulation configs.
37+
┃ ┣ 📁error_analyzer Module for analyzing simulation vs measurement results.
38+
┃ ┃ ┣ 📜config.py Metric and threshold specification.
39+
┃ ┃ ┗ 📜range_checker.py Range/tolerance checking of results.
40+
┃ ┗ 📜README.md Package-level documentation.
41+
┣ 📁tests Pytest test suite for device verification.
42+
┃ ┗ 📜test_devices.py Runs pytest-based device verification.
43+
┗ 📁run_test_flow Example notebooks / workflows.
44+
┗ 📜IHP_devices_testing.ipynb Notebook demonstrating device testing flow.
45+
```
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Makefile for Device Testing (never fails on test errors)
2+
3+
RUNNER ?= verifier
4+
PYTEST_ARGS := --tb=short -p no:capture
5+
6+
# Device configurations
7+
NMOS_LV_CONFIG := mos/nmos_lv/sg13_lv_nmos.yaml
8+
PMOS_LV_CONFIG := mos/pmos_lv/sg13_lv_pmos.yaml
9+
NMOS_HV_CONFIG := mos/nmos_hv/sg13_hv_nmos.yaml
10+
PMOS_HV_CONFIG := mos/pmos_hv/sg13_hv_pmos.yaml
11+
PNP_MPA_CONFIG := pnp_mpa/pnpmpa.yaml
12+
NPN13G2_CONFIG := hbt/npn13g2/npn13g2.yaml
13+
NPN13G2L_CONFIG := hbt/npn13g2l/npn13g2l.yaml
14+
NPN13G2V_CONFIG := hbt/npn13g2v/npn13g2v.yaml
15+
16+
# Groups
17+
MOS_DEVICES := nmos_lv pmos_lv nmos_hv pmos_hv
18+
HBT_DEVICES := npn13g2 npn13g2l npn13g2v
19+
PNP_DEVICES := pnp_mpa
20+
ALL_DEVICES := $(MOS_DEVICES) $(HBT_DEVICES) $(PNP_DEVICES)
21+
22+
.PHONY: help all test-all test-mos test-hbt test-pnp \
23+
nmos_lv pmos_lv nmos_hv pmos_hv pnp_mpa npn13g2 npn13g2l npn13g2v \
24+
pytest-all pytest-mos pytest-hbt pytest-pnp \
25+
verifier-all verifier-mos verifier-hbt verifier-pnp
26+
27+
help:
28+
@echo "Device Testing Makefile"
29+
@echo "===================================="
30+
@echo
31+
@echo "Usage: make [target] [RUNNER=pytest|verifier]"
32+
@echo
33+
@echo "Test Groups:"
34+
@echo " test-all Run all device tests"
35+
@echo " test-mos Run MOS tests (nmos_lv, pmos_lv, nmos_hv, pmos_hv)"
36+
@echo " test-hbt Run HBT tests (npn13g2, npn13g2l, npn13g2v)"
37+
@echo " test-pnp Run PNP tests (pnp_mpa)"
38+
@echo
39+
@echo "Individual Devices:"
40+
@echo " nmos_lv, pmos_lv, nmos_hv, pmos_hv, pnp_mpa, npn13g2, npn13g2l, npn13g2v"
41+
@echo
42+
@echo "Runner-Specific:"
43+
@echo " pytest-all / -mos / -hbt / -pnp"
44+
@echo " verifier-all / -mos / -hbt / -pnp"
45+
@echo
46+
@echo "Options:"
47+
@echo " RUNNER=verifier (default) | pytest"
48+
49+
define run_test
50+
@echo "Running $(1) test with $(RUNNER)..."
51+
@if [ "$(RUNNER)" = "pytest" ]; then \
52+
python3 -m pytest tests/test_devices.py::test_devices[$(1)] $(PYTEST_ARGS) || true; \
53+
else \
54+
python3 -m models_verifier.models_verifier --config $(2) || true; \
55+
fi
56+
endef
57+
58+
nmos_lv:
59+
$(call run_test,nmos_lv,$(NMOS_LV_CONFIG))
60+
pmos_lv:
61+
$(call run_test,pmos_lv,$(PMOS_LV_CONFIG))
62+
nmos_hv:
63+
$(call run_test,nmos_hv,$(NMOS_HV_CONFIG))
64+
pmos_hv:
65+
$(call run_test,pmos_hv,$(PMOS_HV_CONFIG))
66+
pnp_mpa:
67+
$(call run_test,pnp_mpa,$(PNP_MPA_CONFIG))
68+
npn13g2:
69+
$(call run_test,npn13g2,$(NPN13G2_CONFIG))
70+
npn13g2l:
71+
$(call run_test,npn13g2l,$(NPN13G2L_CONFIG))
72+
npn13g2v:
73+
$(call run_test,npn13g2v,$(NPN13G2V_CONFIG))
74+
75+
test-mos:
76+
@if [ "$(RUNNER)" = "pytest" ]; then $(MAKE) pytest-mos; else $(MAKE) verifier-mos; fi
77+
test-hbt:
78+
@if [ "$(RUNNER)" = "pytest" ]; then $(MAKE) pytest-hbt; else $(MAKE) verifier-hbt; fi
79+
test-pnp:
80+
@if [ "$(RUNNER)" = "pytest" ]; then $(MAKE) pytest-pnp; else $(MAKE) verifier-pnp; fi
81+
test-all:
82+
@if [ "$(RUNNER)" = "pytest" ]; then $(MAKE) pytest-all; else $(MAKE) verifier-all; fi
83+
84+
pytest-mos:
85+
@echo "Running MOS device tests with pytest..."
86+
@python3 -m pytest tests/test_devices.py -k "nmos_lv or pmos_lv or nmos_hv or pmos_hv" $(PYTEST_ARGS) || true
87+
pytest-hbt:
88+
@echo "Running HBT device tests with pytest..."
89+
@python3 -m pytest tests/test_devices.py -k "npn13g2 or npn13g2l or npn13g2v" $(PYTEST_ARGS) || true
90+
pytest-pnp:
91+
@echo "Running PNP device tests with pytest..."
92+
@python3 -m pytest tests/test_devices.py -k "pnp_mpa" $(PYTEST_ARGS) || true
93+
pytest-all:
94+
@echo "Running all device tests with pytest..."
95+
@python3 -m pytest tests/test_devices.py $(PYTEST_ARGS) || true
96+
97+
verifier-mos:
98+
@echo "Running MOS device tests with models_verifier..."
99+
@for device in $(MOS_DEVICES); do \
100+
echo "=== Testing $$device ==="; \
101+
case $$device in \
102+
nmos_lv) config=$(NMOS_LV_CONFIG) ;; \
103+
pmos_lv) config=$(PMOS_LV_CONFIG) ;; \
104+
nmos_hv) config=$(NMOS_HV_CONFIG) ;; \
105+
pmos_hv) config=$(PMOS_HV_CONFIG) ;; \
106+
esac; \
107+
python3 -m models_verifier.models_verifier --config $$config || echo "FAILED: $$device"; \
108+
done; true
109+
110+
verifier-hbt:
111+
@echo "Running HBT device tests with models_verifier..."
112+
@for device in $(HBT_DEVICES); do \
113+
echo "=== Testing $$device ==="; \
114+
case $$device in \
115+
npn13g2) config=$(NPN13G2_CONFIG) ;; \
116+
npn13g2l) config=$(NPN13G2L_CONFIG) ;; \
117+
npn13g2v) config=$(NPN13G2V_CONFIG) ;; \
118+
esac; \
119+
python3 -m models_verifier.models_verifier --config $$config || echo "FAILED: $$device"; \
120+
done; true
121+
122+
verifier-pnp:
123+
@echo "Running PNP device tests with models_verifier..."
124+
@python3 -m models_verifier.models_verifier --config $(PNP_MPA_CONFIG) || echo "FAILED: pnp_mpa"; true
125+
126+
verifier-all:
127+
@echo "Running all device tests with models_verifier..."
128+
@$(MAKE) verifier-mos || true; \
129+
$(MAKE) verifier-hbt || true; \
130+
$(MAKE) verifier-pnp || true; \
131+
true
132+
133+
all: test-all
134+
mos: test-mos
135+
hbt: test-hbt
136+
pnp: test-pnp

ihp-sg13g2/libs.tech/ngspice/testing/devices/models_verifier/README.md

Lines changed: 80 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,147 @@
1-
# MDM DC verification – quick guide
21

3-
This script runs the MDM aggregation → DC simulation → envelope checks using the YAML configs for each device.
4-
## Environment setup
2+
# MDM DC Verification – Quick Guide
53

6-
Before running, make sure you have:
4+
This script runs the flow:
75

8-
* **Python 3.8+** installed
9-
* **ngspice** available on your system (required for simulations)
10-
* **openvaf** installed and in your PATH (needed to compile Verilog-A models)
6+
**MDM aggregation → DC simulation → Envelope checks**
7+
using the YAML configs for each device.
118

12-
### Build OSDI files (once per repo checkout)
9+
10+
11+
## 1. Environment Setup
12+
13+
### Requirements
14+
- **Python 3.8+**
15+
- **ngspice** (required for simulations)
16+
- **openvaf** in your `PATH` (needed to compile Verilog-A models)
17+
18+
### Build OSDI Files (once per repo checkout)
1319

1420
```bash
1521
cd <repo path>/ihp-sg13g2/libs.tech/verilog-a
1622
chmod +x openvaf-compile-va.sh
1723
./openvaf-compile-va.sh
24+
````
1825

19-
# Verify OSDI files were built
20-
ls -la <repo path>/ihp-sg13g2/libs.tech/ngspice/osdi
21-
```
22-
### Create a Python virtual environment and install dependencies
26+
### Create Virtual Environment & Install Dependencies
2327

2428
```bash
2529
# Create and activate venv
2630
python3 -m venv .venv
2731
source .venv/bin/activate
2832
29-
30-
# Install required Python packages
33+
# Install required packages
3134
pip install pandas pyyaml jinja2 pytest
3235
```
3336

34-
## Run commands
37+
---
38+
39+
## 2. Running Tests
3540

36-
> Run from the **devices** folder so the relative paths in the configs resolve correctly.
41+
> Always run from the **devices** folder so relative paths in configs resolve correctly.
3742

3843
```bash
3944
cd ihp-sg13g2/libs.tech/ngspice/testing/devices
4045
```
41-
```bash
42-
export IHP_OPEN_REPO=<repo path>
43-
```
44-
### NMOS (sg13_lv_nmos)
45-
```bash
46-
python3 -m models_verifier.models_verifier -c mos/nmos_lv/sg13_lv_nmos.yaml
47-
```
4846

49-
### PMOS (sg13_lv_pmos)
47+
### Run a Single Device
5048

51-
```bash
52-
python3 -m models_verifier.models_verifier -c mos/pmos_lv/sg13_lv_pmos.yaml
53-
```
49+
* **NMOS (sg13\_lv\_nmos)**
50+
51+
```bash
52+
python3 -m models_verifier.models_verifier -c mos/nmos_lv/sg13_lv_nmos.yaml
53+
```
5454

55-
## Outputs
55+
* **PMOS (sg13\_lv\_pmos)**
5656

57-
When a run finishes you will see:
57+
```bash
58+
python3 -m models_verifier.models_verifier -c mos/pmos_lv/sg13_lv_pmos.yaml
59+
```
5860

59-
1. Per-setup merged CSVs (for debugging/inspection)
61+
(Other devices: `nmos_hv`, `pmos_hv`, `pnp_mpa`, `npn13g2`, `npn13g2l`, `npn13g2v`)
6062

61-
- Directory: `<output_dir>/sim_merged/`
62-
- One CSV per discovered sweep/setup (filename derived from `master_setup_type`)
6363

64-
2. Reports (per the `output_dir` in the YAML)
64+
## 3. Outputs
6565

66-
- **Full summary:** `<output_dir>/full_report.csv`
66+
When a run finishes, you will see:
67+
68+
### 3.1 Per-setup merged CSVs (for debugging/inspection)
69+
70+
* Location: `<output_dir>/sim_merged/`
71+
* One CSV per discovered sweep/setup
72+
(filename derived from `master_setup_type`)
73+
74+
### 3.2 Reports (per `output_dir` in YAML)
75+
76+
* **Full summary:** `<output_dir>/full_report.csv`
6777
One row per `(block_id, metric, target)` with counts and pass/fail.
68-
- **Roll-up summary:** `<output_dir>/summary.csv`
78+
79+
* **Roll-up summary:** `<output_dir>/summary.csv`
6980
Aggregated by `(metric, target)` with overall out-of-bounds percentages.
70-
- **Detailed failures:** `<output_dir>/detailed_failures.csv` _(only if there are any)_
71-
Row per failing point with value, bounds, and basic context.
7281

73-
The script also prints a short summary block to the terminal, including total cases, per-target pass/fail counts, and the number of failing points.
82+
* **Detailed failures:** `<output_dir>/detailed_failures.csv`
83+
Only written if failures exist.
84+
Row per failing point with value, bounds, and context.
85+
86+
Additionally, the script prints a **summary block to the terminal**, including:
7487

75-
## Exit status
88+
* Total cases
89+
* Per-target pass/fail counts
90+
* Number of failing points
7691

77-
- `0` → all selected targets in the run passed their thresholds
78-
- `1` → one or more groups failed (reports still written)
7992

80-
(Other non-zero exit codes indicate early termination before reporting.)
93+
## 4. Exit Status Codes
8194

82-
## Interpreting pytest assertion failures
95+
* **0** → All selected targets passed thresholds
96+
* **1** → One or more groups failed (reports still written)
97+
* **Other non-zero** → Early termination before reporting
8398

84-
When you run the tests , a device test fails if any checked group exceeds the configured out-of-range threshold. In that case, pytest raises an **AssertionError** and prints lines like:
99+
---
100+
101+
## 5. Interpreting Pytest Failures
102+
103+
When using pytest, a device test fails if any checked group exceeds the configured out-of-range threshold. Example output:
85104

86105
```
87106
check failed:
88107
[id/meas] (FAIL file=/path/to/meas.mdm, block_index=42) n=120 out_of_bounds=7 (5.83%)
89108
[ib/tt] (FAIL file=/path/to/meas.mdm, block_index=7) n=95 out_of_bounds=6 (6.32%)
90-
...
91109
92110
STATUS: 2/24 groups FAILED (8.33%); pass rate = 91.67%
93111
- id/meas: 1 fails
94-
- ib/tt: 1 fails
112+
- ib/tt: 1 fails
95113
```
96114

97-
### What each part means
115+
### Meaning of Each Part
116+
117+
* `[metric/target]` — Metric and target (`meas` = measured, `tt` = typical).
118+
* `file=...` — Source MDM file.
119+
* `block_index=...` — Block index inside that file.
120+
* `n=` — Total points in that group.
121+
* `out_of_bounds=` — Number outside allowed envelope.
122+
* `(%)` — Percent of failing points.
123+
* `STATUS:` — Summary across groups.
124+
* Bullet list — Per-metric breakdown of failures.
98125

99-
- `[metric/target]` — which metric and target failed (`meas` = measured, `tt` = typical corner).
100-
- `file=...` — the source mdm that produced the failing group (if available).
101-
- `block_index=...` — the block index within that file (if available).
102-
- `n=` — total points evaluated in that group.
103-
- `out_of_bounds=` — number of points outside the allowed envelope.
104-
- `(%)` — percent of points outside the envelope for that group.
105-
- `STATUS:` — summary across all groups: how many failed vs. total, plus pass rate.
106-
- Per-metric lines (`- <metric>/<target>: <count> fails`) — quick breakdown by metric/target.
126+
---
107127

108-
## Run locally
128+
## 6. Running Locally (CI-style)
109129

110-
### Run a single device case (same as CI)
130+
### Run a Single Device with Pytest
111131

112132
```bash
113133
cd ihp-sg13g2/libs.tech/ngspice/testing/devices
114134
python3 -m pytest --tb=short -p no:capture \
115135
'tests/test_devices.py::test_devices[nmos_lv]'
116136
```
117137

118-
Replace `nmos_lv` with any of: `pmos_lv`, `nmos_hv`, `pmos_hv`, `pnp_mpa`, `npn13g2`, `npn13g2l`, `npn13g2h`.
119-
138+
Replace `nmos_lv` with any of:
139+
`pmos_lv`, `nmos_hv`, `pmos_hv`, `pnp_mpa`, `npn13g2`, `npn13g2l`, `npn13g2v`
120140

121-
### Run all tests
141+
### Run All Devices
122142

123143
```bash
124144
cd ihp-sg13g2/libs.tech/ngspice/testing/devices
125145
python3 -m pytest --tb=short -p no:capture tests/test_devices.py
126146
```
127147

128-
---
129-

0 commit comments

Comments
 (0)