Module pyspectre.core
Python interface for Cadence Spectre
Expand source code
""" Python interface for Cadence Spectre """
import os
import re
from subprocess import run, DEVNULL
from tempfile import NamedTemporaryFile
import errno
import warnings
from collections.abc import Iterable
from typing import NamedTuple, NewType
import pexpect
from pandas import DataFrame
from pynut import read_raw, plot_dict
def netlist_to_tmp(netlist: str) -> str:
"""
Write a netlist to a temporary file
"""
tmp = NamedTemporaryFile(mode = 'w', suffix = '.scs', delete = False)
path = tmp.name
tmp.write(netlist)
tmp.close()
return path
def raw_tmp(net_path: str) -> str:
"""
Raw simulation results in /tmp
"""
pre = f'{os.path.splitext(os.path.basename(net_path))[0]}'
suf = '.raw'
tmp = NamedTemporaryFile(prefix = pre, suffix = suf, delete = False)
path = tmp.name
tmp.close()
return path
def read_results(raw_file: str) -> dict[str, DataFrame]:
"""
Read simulation results
"""
if not os.path.isfile(raw_file):
raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), raw_file))
if not os.access(raw_file, os.R_OK):
raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), raw_file))
return plot_dict(read_raw(raw_file))
def simulate( netlist_path: str, includes: Iterable[str] = None
, raw_path: str = None ) -> dict[str, DataFrame]:
"""
Passes the given netlist path to spectre and reads the results in.
"""
net = os.path.expanduser(netlist_path)
inc = [f'-I{os.path.expanduser(i)}' for i in includes]
raw = raw_path or raw_tmp(net)
cmd = ['spectre', '-64', '-format nutbin', f'-raw {raw}', '-log'
] + inc + [net]
if not os.path.isfile(net):
raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), net))
if not os.access(net, os.R_OK):
raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), net))
#ret = os.system(cmd)
ret = run( cmd
, check = False
, stdin = DEVNULL
, stdout = DEVNULL
, stderr = DEVNULL
, capture_output = False
, ).returncode
if ret != 0:
raise(IOError(errno.EIO, os.strerror(errno.EIO), 'spectre'))
if not os.path.isfile(raw):
raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), raw))
if not os.access(raw, os.R_OK):
raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), raw))
return read_results(raw)
def simulate_netlist(netlist: str, **kwargs) -> dict[str, DataFrame]:
"""
Takes a netlist as text, creates a temporary file and simulates it. The
results are read in and all temp files will be destroyed.
"""
path = netlist_to_tmp(netlist)
ret = simulate(path, **kwargs)
_ = os.remove(path)
return ret
REPL = NewType('REPL', pexpect.spawn)
Session = NamedTuple( 'Session'
, [ ( 'net_file', str)
, ( 'raw_file', str)
, ( 'repl' , REPL)
, ( 'prompt' , str )
, ( 'succ' , str )
, ( 'fail' , str ) ]
, )
def start_session( net_path: str, includes: list[str] = None
, raw_path: str = None ) -> Session:
"""
Start spectre interactive session
"""
prompt = '\r\n>\s'
succ = '.*\nt'
fail = '.*\nnil'
net = os.path.expanduser(net_path)
raw = raw_path or raw_tmp(net)
inc = [] if not includes else [f'-I{os.path.expanduser(i)}' for i in includes]
cmd = 'spectre'
args = ['-64', '+interactive', '-format nutbin', f'-raw {raw}', '-log'
] + inc + [net]
if not os.path.isfile(net):
raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), net))
if not os.access(net, os.R_OK):
raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), net))
repl = pexpect.spawn(cmd, args)
if repl.expect(prompt) != 0:
raise(IOError(errno.EIO, os.strerror(errno.EIO), cmd))
return Session(net_path, raw, repl, prompt, succ, fail)
def run_command(session: Session, command: str) -> bool:
"""
Internal function for running an arbitrary scl command. Returns True or
false based on what the previous command returned.
"""
session.repl.sendline(command)
return session.repl.expect(session.prompt) == 0
def run_all(session: Session) -> dict[str, DataFrame]:
"""
Run all simulation analyses
"""
run_command(session, '(sclRun "all")')
return read_results(session.raw_file)
def get_analyses(session: Session) -> dict[str, str]:
"""
Retrieve all simulation analyses from current netlist
"""
cmd = '(sclListAnalysis)'
run_command(session, cmd)
out = session.repl.before.decode('utf-8').split('\n')[1:-1]
return dict([re.search(r'"(.+)"\s*"(.+)"', o).groups() for o in out])
def run_analysis(session: Session, analysis: str) -> dict[str, DataFrame]:
"""
Run only the given analysis.
"""
cmd = f'(sclRunAnalysis (sclGetAnalysis "{analysis}"))'
run_command(session, cmd)
return read_results(session.raw_file)
def set_parameter(session: Session, param: str, value: float) -> bool:
"""
Change a parameter in the netlist. Returns True if successful, False
otherwise.
"""
cmd = f'(sclSetAttribute (sclGetParameter (sclGetCircuit "") "{param}") "value" {value})'
return run_command(session, cmd)
def set_parameters(session: Session, params: dict[str, float]) -> bool:
"""
Set a list of parameters
"""
return all(set_parameter(session, p, v) for p,v in params.items())
def get_parameter(session: Session, param: str) -> float:
"""
Get a parameter in the netlist.
"""
cmd = f'(sclGetAttribute (sclGetParameter (sclGetCircuit "") "{param}") "value")'
run_command(session, cmd)
return float(session.repl.before.decode('utf-8').split('\n')[-1])
def get_parameters(session: Session, params: Iterable[str]) -> dict[str, float]:
"""
Get a set of parameters in the netlist.
"""
return {param: get_parameter(session, param) for param in params}
def stop_session(session, remove_raw: bool = False) -> bool:
"""
Quit spectre interactive session and close terminal
"""
if session.repl.isalive():
session.repl.sendline('(sclQuit)')
session.repl.wait()
if session.repl.isalive():
warnings.warn( 'spectre refused to exit gracefully, forcing ...'
, RuntimeWarning )
session.repl.terminate(force = True)
if remove_raw:
os.remove(session.raw_file)
return not session.repl.isalive()
Functions
def get_analyses(session: Session) ‑> dict[str, str]
-
Retrieve all simulation analyses from current netlist
Expand source code
def get_analyses(session: Session) -> dict[str, str]: """ Retrieve all simulation analyses from current netlist """ cmd = '(sclListAnalysis)' run_command(session, cmd) out = session.repl.before.decode('utf-8').split('\n')[1:-1] return dict([re.search(r'"(.+)"\s*"(.+)"', o).groups() for o in out])
def get_parameter(session: Session, param: str) ‑> float
-
Get a parameter in the netlist.
Expand source code
def get_parameter(session: Session, param: str) -> float: """ Get a parameter in the netlist. """ cmd = f'(sclGetAttribute (sclGetParameter (sclGetCircuit "") "{param}") "value")' run_command(session, cmd) return float(session.repl.before.decode('utf-8').split('\n')[-1])
def get_parameters(session: Session, params: collections.abc.Iterable[str]) ‑> dict[str, float]
-
Get a set of parameters in the netlist.
Expand source code
def get_parameters(session: Session, params: Iterable[str]) -> dict[str, float]: """ Get a set of parameters in the netlist. """ return {param: get_parameter(session, param) for param in params}
def netlist_to_tmp(netlist: str) ‑> str
-
Write a netlist to a temporary file
Expand source code
def netlist_to_tmp(netlist: str) -> str: """ Write a netlist to a temporary file """ tmp = NamedTemporaryFile(mode = 'w', suffix = '.scs', delete = False) path = tmp.name tmp.write(netlist) tmp.close() return path
def raw_tmp(net_path: str) ‑> str
-
Raw simulation results in /tmp
Expand source code
def raw_tmp(net_path: str) -> str: """ Raw simulation results in /tmp """ pre = f'{os.path.splitext(os.path.basename(net_path))[0]}' suf = '.raw' tmp = NamedTemporaryFile(prefix = pre, suffix = suf, delete = False) path = tmp.name tmp.close() return path
def read_results(raw_file: str) ‑> dict[str, pandas.core.frame.DataFrame]
-
Read simulation results
Expand source code
def read_results(raw_file: str) -> dict[str, DataFrame]: """ Read simulation results """ if not os.path.isfile(raw_file): raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), raw_file)) if not os.access(raw_file, os.R_OK): raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), raw_file)) return plot_dict(read_raw(raw_file))
def run_all(session: Session) ‑> dict[str, pandas.core.frame.DataFrame]
-
Run all simulation analyses
Expand source code
def run_all(session: Session) -> dict[str, DataFrame]: """ Run all simulation analyses """ run_command(session, '(sclRun "all")') return read_results(session.raw_file)
def run_analysis(session: Session, analysis: str) ‑> dict[str, pandas.core.frame.DataFrame]
-
Run only the given analysis.
Expand source code
def run_analysis(session: Session, analysis: str) -> dict[str, DataFrame]: """ Run only the given analysis. """ cmd = f'(sclRunAnalysis (sclGetAnalysis "{analysis}"))' run_command(session, cmd) return read_results(session.raw_file)
def run_command(session: Session, command: str) ‑> bool
-
Internal function for running an arbitrary scl command. Returns True or false based on what the previous command returned.
Expand source code
def run_command(session: Session, command: str) -> bool: """ Internal function for running an arbitrary scl command. Returns True or false based on what the previous command returned. """ session.repl.sendline(command) return session.repl.expect(session.prompt) == 0
def set_parameter(session: Session, param: str, value: float) ‑> bool
-
Change a parameter in the netlist. Returns True if successful, False otherwise.
Expand source code
def set_parameter(session: Session, param: str, value: float) -> bool: """ Change a parameter in the netlist. Returns True if successful, False otherwise. """ cmd = f'(sclSetAttribute (sclGetParameter (sclGetCircuit "") "{param}") "value" {value})' return run_command(session, cmd)
def set_parameters(session: Session, params: dict[str, float]) ‑> bool
-
Set a list of parameters
Expand source code
def set_parameters(session: Session, params: dict[str, float]) -> bool: """ Set a list of parameters """ return all(set_parameter(session, p, v) for p,v in params.items())
def simulate(netlist_path: str, includes: collections.abc.Iterable[str] = None, raw_path: str = None) ‑> dict[str, pandas.core.frame.DataFrame]
-
Passes the given netlist path to spectre and reads the results in.
Expand source code
def simulate( netlist_path: str, includes: Iterable[str] = None , raw_path: str = None ) -> dict[str, DataFrame]: """ Passes the given netlist path to spectre and reads the results in. """ net = os.path.expanduser(netlist_path) inc = [f'-I{os.path.expanduser(i)}' for i in includes] raw = raw_path or raw_tmp(net) cmd = ['spectre', '-64', '-format nutbin', f'-raw {raw}', '-log' ] + inc + [net] if not os.path.isfile(net): raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), net)) if not os.access(net, os.R_OK): raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), net)) #ret = os.system(cmd) ret = run( cmd , check = False , stdin = DEVNULL , stdout = DEVNULL , stderr = DEVNULL , capture_output = False , ).returncode if ret != 0: raise(IOError(errno.EIO, os.strerror(errno.EIO), 'spectre')) if not os.path.isfile(raw): raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), raw)) if not os.access(raw, os.R_OK): raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), raw)) return read_results(raw)
def simulate_netlist(netlist: str, **kwargs) ‑> dict[str, pandas.core.frame.DataFrame]
-
Takes a netlist as text, creates a temporary file and simulates it. The results are read in and all temp files will be destroyed.
Expand source code
def simulate_netlist(netlist: str, **kwargs) -> dict[str, DataFrame]: """ Takes a netlist as text, creates a temporary file and simulates it. The results are read in and all temp files will be destroyed. """ path = netlist_to_tmp(netlist) ret = simulate(path, **kwargs) _ = os.remove(path) return ret
def start_session(net_path: str, includes: list[str] = None, raw_path: str = None) ‑> Session
-
Start spectre interactive session
Expand source code
def start_session( net_path: str, includes: list[str] = None , raw_path: str = None ) -> Session: """ Start spectre interactive session """ prompt = '\r\n>\s' succ = '.*\nt' fail = '.*\nnil' net = os.path.expanduser(net_path) raw = raw_path or raw_tmp(net) inc = [] if not includes else [f'-I{os.path.expanduser(i)}' for i in includes] cmd = 'spectre' args = ['-64', '+interactive', '-format nutbin', f'-raw {raw}', '-log' ] + inc + [net] if not os.path.isfile(net): raise(FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), net)) if not os.access(net, os.R_OK): raise(PermissionError(errno.EACCES, os.strerror(errno.EACCES), net)) repl = pexpect.spawn(cmd, args) if repl.expect(prompt) != 0: raise(IOError(errno.EIO, os.strerror(errno.EIO), cmd)) return Session(net_path, raw, repl, prompt, succ, fail)
def stop_session(session, remove_raw: bool = False) ‑> bool
-
Quit spectre interactive session and close terminal
Expand source code
def stop_session(session, remove_raw: bool = False) -> bool: """ Quit spectre interactive session and close terminal """ if session.repl.isalive(): session.repl.sendline('(sclQuit)') session.repl.wait() if session.repl.isalive(): warnings.warn( 'spectre refused to exit gracefully, forcing ...' , RuntimeWarning ) session.repl.terminate(force = True) if remove_raw: os.remove(session.raw_file) return not session.repl.isalive()
Classes
class Session (net_file: str, raw_file: str, repl: REPL, prompt: str, succ: str, fail: str)
-
Session(net_file, raw_file, repl, prompt, succ, fail)
Ancestors
- builtins.tuple
Instance variables
var fail : str
-
Alias for field number 5
var net_file : str
-
Alias for field number 0
var prompt : str
-
Alias for field number 3
var raw_file : str
-
Alias for field number 1
var repl : REPL
-
Alias for field number 2
var succ : str
-
Alias for field number 4