Browse Source

Add XSPICE test suite and update CLAUDE.md with code model docs

- Add tests/test_xspice.py: 10 tests covering .cm plugin loading (all 7
  categories parametrized) and gain model simulation verification
- Update CLAUDE.md with XSPICE code model section documenting available
  libraries, build_cmpp.py usage, and Verilog co-simulation status
- Remove scratch files from repo root (test.c, test_build.bat, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pre-master-46
Joe DiPrima 1 day ago
parent
commit
7461227001
  1. 36
      CLAUDE.md
  2. 109
      tests/test_xspice.py

36
CLAUDE.md

@ -138,12 +138,46 @@ option(ENABLE_KLU "Enable KLU sparse matrix solver" ON)
```
And ensure `visualc/src/include/ngspice/config.h` matches (e.g., `#define CIDER`).
## XSPICE Code Model Plugins
Pre-built `.cm` plugin libraries ship in `pyngspice/codemodels/` and are loaded at runtime:
```python
sim.command("codemodel path/to/analog.cm") # Gain, summer, limiter, etc.
sim.command("codemodel path/to/digital.cm") # d_and, d_or, d_cosim (Verilog co-sim), etc.
```
### Available Libraries
| Library | Models | Description |
|---------|--------|-------------|
| analog.cm | 23 | Analog blocks (gain, summer, limiter, s_xfer, etc.) |
| digital.cm | 32 | Digital gates, flip-flops, d_cosim (Verilog co-simulation) |
| spice2poly.cm | — | SPICE2 polynomial source compatibility |
| table.cm | — | Table-based model interpolation |
| tlines.cm | 5 | Transmission line models |
| xtradev.cm | 13 | Magnetic core, inductor w/ saturation, zener, memristor |
| xtraevt.cm | 4+2 | Event-driven models + UDN types (int, real) |
### Building Code Models
```bash
python build_cmpp.py analog digital spice2poly table tlines xtradev xtraevt
```
Requires MinGW gcc. Runs cmpp preprocessor on `.ifs`/`.mod` source files, then compiles
into `.cm` DLLs. See `XSPICE_CODE_MODELS.md` for how to write custom code models.
### Verilog Co-Simulation
`d_cosim` (in digital.cm) loads Verilator-compiled Verilog designs as DLLs at runtime.
See `src/xspice/verilog/verilator_shim.cpp` and `examples/xspice/verilator/` for details.
Requires Verilator + Perl to compile `.v``.dll`. The ngspice side is ready; pyTesla
owns the compilation pipeline and UI integration.
## Testing
```bash
pytest tests/ -v # All tests
pytest tests/ -v # All tests (95 tests)
pytest tests/test_netlist.py -v # Netlist pre-processor only (no build needed)
pytest tests/test_runner.py tests/test_sim.py -v # Integration tests (need built extension)
pytest tests/test_xspice.py -v # XSPICE code model loading + simulation tests
```
### Requirements

109
tests/test_xspice.py

@ -0,0 +1,109 @@
"""Tests for XSPICE code model plugin loading and simulation."""
import os
import pytest
from pyngspice import _cpp_available
def _get_codemodel_dir():
"""Return the path to pre-built .cm files."""
import pyngspice
return os.path.join(os.path.dirname(pyngspice.__file__), "codemodels")
def _cm_path(name):
"""Return full path to a .cm file."""
return os.path.join(_get_codemodel_dir(), f"{name}.cm")
CM_CATEGORIES = ["analog", "digital", "spice2poly", "table", "tlines", "xtradev", "xtraevt"]
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built")
class TestCodeModelLoading:
"""Test that all pre-built .cm plugins load successfully."""
@pytest.mark.parametrize("category", CM_CATEGORIES)
def test_load_cm(self, category):
"""Each .cm plugin should exist and load without error."""
from pyngspice import Simulator
path = _cm_path(category)
assert os.path.exists(path), f"{category}.cm not found at {path}"
assert os.path.getsize(path) > 0, f"{category}.cm is empty"
sim = Simulator()
sim.initialize()
ok = sim.command(f"codemodel {path}")
assert ok, f"Failed to load {category}.cm"
def test_load_all(self):
"""All 7 .cm plugins should load into a single simulator instance."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
for category in CM_CATEGORIES:
path = _cm_path(category)
if os.path.exists(path):
ok = sim.command(f"codemodel {path}")
assert ok, f"Failed to load {category}.cm"
@pytest.mark.skipif(not _cpp_available, reason="C++ extension not built")
class TestCodeModelSimulation:
"""Test running simulations with XSPICE code models."""
def _load_analog(self, sim):
"""Load analog.cm into a simulator instance."""
path = _cm_path("analog")
if not os.path.exists(path):
pytest.skip("analog.cm not built")
sim.command(f"codemodel {path}")
def test_gain_dc(self):
"""Gain block with gain=3.0 should triple the input voltage."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
self._load_analog(sim)
sim.command("circbyline Gain DC Test")
sim.command("circbyline V1 in 0 DC 2.0")
sim.command("circbyline A1 in out gain_model")
sim.command("circbyline .model gain_model gain(gain=3.0)")
sim.command("circbyline R1 out 0 1k")
sim.command("circbyline .dc V1 0 5 1")
sim.command("circbyline .end")
sim.command("run")
vecs = sim.all_vectors()
assert "out" in vecs
assert "in" in vecs
def test_gain_sweep_values(self):
"""Verify gain block output values match expected gain * input."""
from pyngspice import Simulator
sim = Simulator()
sim.initialize()
self._load_analog(sim)
sim.command("circbyline Gain Sweep Test")
sim.command("circbyline V1 in 0 DC 1.0")
sim.command("circbyline A1 in out gain_model")
sim.command("circbyline .model gain_model gain(gain=2.0)")
sim.command("circbyline R1 out 0 1k")
sim.command("circbyline .dc V1 0 5 1")
sim.command("circbyline .end")
sim.command("run")
# Use ngspice print command and check output
sim.clear_output()
sim.command("print out")
output = sim.get_output()
# Output should contain values that are 2x the input
assert "out" in output.lower()
Loading…
Cancel
Save