Metadata-Version: 2.4
Name: analogpy
Version: 0.1.0
Summary: Analog circuit IR (Intermediate Representation) and Spectre netlist generator
Author-email: Gaofeng Fan <circuitmuggle@gmaigmaill.com>
License: LICENSE
Project-URL: Homepage, https://github.com/circuitmuggle/analogpy
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Dynamic: license-file

License: Apache-2.0

# analog-py

Python DSL + AST + Codegen for Analog Circuit Design and Netlist Generation.

## Project Goals

**analogpy** is a Python library for generating circuit netlists. It bridges the gap between Python programming and analog circuit simulation.

### What analogpy DOES:

1. **Generate netlists** (MVP: Spectre, future: ngspice)
   - Circuit topology in Python
   - Hierarchical subcircuits
   - Testbench with analyses

2. **Build simulation commands** (not execute)
   - SpectreCommand builder with configurable options
   - User executes via tmux-ssh, LSCS, ICRP, or any job system

3. **Parse simulation results** (planned)
   - Read PSF/nutbin files
   - Expose data as numpy arrays / pandas DataFrames
   - Enable Python-native post-processing

4. **Make Python loop design easy**
   - PVT corners: Python loop generates N netlists
   - Monte Carlo: Python loop with different seeds
   - Parameter sweeps: Python variables directly in netlist

### What analogpy does NOT do:

- **Job submission**: Use Python + tmux-ssh / LSCS / ICRP
- **Heavy analysis**: Use numpy, scipy (FFT, filtering, etc.)
- **Visualization**: Use matplotlib, plotly (analogpy provides helpers)
- **Replace Cadence ADE**: analogpy is CLI/script-first, not GUI

### Design Philosophy

```
┌─────────────────────────────────────────────────────────────┐
│                      Python Script                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │  analogpy   │  │   numpy     │  │    matplotlib       │  │
│  │  (netlist)  │  │   scipy     │  │    plotly           │  │
│  │  (parse)    │  │   pandas    │  │    (visualization)  │  │
│  │  (expose)   │  │  (analysis) │  │                     │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
└─────────┼────────────────┼───────────────────┼──────────────┘
          │                │                   │
          ▼                ▼                   ▼
    ┌──────────┐    ┌──────────────┐    ┌───────────┐
    │ Spectre  │    │ Post-process │    │  Plots    │
    │ Netlist  │    │ (FFT, etc.)  │    │ PNG/HTML  │
    └──────────┘    └──────────────┘    └───────────┘
```

## Roadmap

- 0.1.x AST + netlist generation ✅
- 0.2.x Result parser + data exposure
- 0.3.x Optimization / AI hooks
- 1.0.0 Stable IR

## Installation

```bash
pip install -e .
```

## Quick Start

```python
from analogpy import Circuit, Subcircuit, nmos, pmos, generate_spectre

# Define a reusable inverter subcircuit
inv = Subcircuit("inverter", ports=["in", "out", "vdd", "gnd"])
inv.add(nmos("MN", d=inv.net("out"), g=inv.net("in"),
             s=inv.net("gnd"), b=inv.net("gnd"), w=1e-6, l=180e-9))
inv.add(pmos("MP", d=inv.net("out"), g=inv.net("in"),
             s=inv.net("vdd"), b=inv.net("vdd"), w=2e-6, l=180e-9))

# Create top-level testbench
top = Circuit("tb_inverter")
vin = top.net("vin")
vout = top.net("vout")
vdd = top.supply("vdd", 1.8)
gnd = top.gnd()

# Instantiate the inverter
top.instantiate(inv, "X1", **{"in": vin, "out": vout, "vdd": vdd, "gnd": gnd})

# Generate Spectre netlist
netlist = generate_spectre(top)
print(netlist)
```

## Examples

See the `examples/` folder for complete workflows:

- `examples/01_inverter_basic.py` - Simple inverter netlist
- `examples/02_ota_testbench.py` - OTA with DC/AC analysis
- `examples/03_pvt_sweep.py` - PVT corner sweep with Python loop
- `examples/04_monte_carlo.py` - Monte Carlo with Python loop
- `examples/05_result_processing.py` - Parse results and plot (planned)

## Features

### Phase 1: Core Hierarchy (Implemented)

- **Circuit**: Top-level circuit container with nets, instances, supplies
- **Subcircuit**: Reusable circuit blocks with defined ports
- **Instantiation**: Hierarchical design with `circuit.instantiate()`
- **Nested hierarchy**: Subcircuits can contain other subcircuits

### Phase 2: Testbench & Analysis (Implemented)

- **Testbench**: Test environment extending Circuit with simulation setup
- **Analysis classes**: DC, AC, Transient, Noise, STB
- **Simulator options**: Temperature, tolerances, convergence settings
- **Behavioral models**: Verilog-A include support

```python
from analogpy import Testbench, DC, AC, Transient

tb = Testbench("tb_amp")
tb.supply("vdd", 1.8)
tb.set_temp(27)
tb.add_analysis(DC())
tb.add_analysis(AC(start=1, stop=1e9, points=100))
tb.add_analysis(Transient(stop=1e-6))
```

### Phase 3: SaveConfig (Implemented)

- **Hierarchical saves**: Define saves at block level, apply with prefix
- **Tagged signals**: Filter saves by category
- **Testbench control**: Override, include, exclude saves

```python
from analogpy import SaveConfig

# Define saves for OTA block
ota_saves = (SaveConfig("ota")
    .voltage("out", "tail", tag="essential")
    .op("M1:gm", "M2:gm", tag="op_params"))

# In testbench, apply with hierarchy prefix
tb.save(ota_saves.with_prefix("X_LDO.X_OTA"))
```

### Phase 4: Device Primitives (Implemented)

- **MOSFETs**: `nmos()`, `pmos()` with nf support
- **Passives**: `resistor()`, `capacitor()`, `inductor()`
- **Sources**: `vsource()`, `isource()`, `vpulse()`, `vsin()`
- **Controlled sources**: `vcvs()`, `vccs()`, `ccvs()`, `cccs()`
- **Other**: `diode()`

### Phase 5: SpectreCommand (Implemented)

- **Command builder**: Generate spectre commands without execution
- **Configurable**: Accuracy, threads, timeouts, include paths
- **Presets**: Liberal (fast), conservative (robust), moderate

```python
from analogpy import SpectreCommand

cmd = (SpectreCommand("input.scs")
    .accuracy("liberal")
    .threads(16)
    .include_path("/path/to/models")
    .build())

# User executes: tmux-ssh "cmd" or submit to LSCS/ICRP
```

### Phase 6: SimulationBatch (Implemented)

- **PVT sweeps**: Process/Voltage/Temperature corners
- **Monte Carlo**: Generate N runs with different seeds
- **Runner scripts**: Python scripts with CLI configuration

```python
from analogpy import SimulationBatch

# Python loop generates multiple netlists
batch = SimulationBatch("ldo_pvt", "/sim/ldo_pvt")
batch.pvt_sweep(make_tb_ldo, corners=[
    {"process": "tt", "voltage": 1.8, "temp": 27},
    {"process": "ff", "voltage": 1.98, "temp": -40},
    {"process": "ss", "voltage": 1.62, "temp": 125},
])
batch.command_options(accuracy="liberal", threads=16)
batch.generate()
batch.write_runner("run_pvt.py")

# User runs: python run_pvt.py commands | parallel tmux-ssh {}
```

### Phase 7: PDK Infrastructure (Implemented)

- **PDK loader**: Load PDK configuration by name
- **Multi-source config**: Project, user, environment variables
- **NDA-safe**: PDK files never included in package

```python
from analogpy.pdk import PDK

pdk = PDK.load("tsmc28")  # Loads from config
mn1 = pdk.nmos("M1", d=vout, g=vin, s=gnd, b=gnd, w=1e-6, l=28e-9, nf=4)
```

### Phase 8: Result Parsing (Planned)

- **Parse PSF/nutbin**: Read Spectre output files
- **Expose as Python data**: numpy arrays, pandas DataFrames
- **Display config**: Separate from save config
- **Validation**: Warn if display signal not in saved signals

```python
# Planned API
from analogpy.results import load_results

results = load_results("/sim/ldo_pvt/tt_v1.8_t27/psf")

# Point query
vout_dc = results.dc["X_OTA.vout"]

# Waveform as numpy array
vout_tran = results.tran["vout"]  # Returns (time, values) arrays

# At specific time
vgs_at_10ns = results.tran["M1:vgs"].at(10e-9)

# Use Python for analysis
import numpy as np
from scipy.fft import fft

spectrum = fft(vout_tran.values)  # numpy/scipy does the work
```

## Architecture

```
analogpy/
├── circuit.py      # Circuit, Subcircuit, Net, Instance
├── devices.py      # nmos, pmos, resistor, capacitor, etc.
├── spectre.py      # Spectre netlist generation
├── testbench.py    # Testbench class
├── analysis.py     # DC, AC, Transient, Noise, STB
├── save.py         # SaveConfig for probe management
├── command.py      # SpectreCommand builder
├── batch.py        # SimulationBatch for PVT/MC
├── pdk/            # PDK loader infrastructure
└── results/        # Result parsing (planned)
```

## Design Principles

1. **Netlist-focused**: Generate netlists, expose results - that's it
2. **Python-native**: Use Python variables, loops, data structures
3. **Don't reinvent**: FFT? Use scipy. Plots? Use matplotlib.
4. **CLI-first**: No GUI, scripts and commands
5. **AI-friendly**: Simple patterns for LLM generation

## Testing

```bash
pytest tests/ -v
```

## License

Apache-2.0
