Browse Source
Add XSPICE test suite and update CLAUDE.md with code model docs
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
2 changed files with 144 additions and 1 deletions
-
36CLAUDE.md
-
109tests/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() |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue