(testing) simplify test cases, fix bug

This commit is contained in:
Markus Mueller 2022-05-10 11:22:25 +02:00 committed by DSPOM
parent deb33cbe8e
commit 92aea309ec
11 changed files with 519 additions and 311 deletions

View File

@ -0,0 +1,367 @@
/*
* Copyright© 2022 SemiMod UG. All rights reserved.
*
* This is an exemplary implementation of the OSDI interface for the Verilog-A
* model specified in diode.va. In the future, the OpenVAF compiler shall
* generate an comparable object file. Primary purpose of this is example to
* have a concrete example for the OSDI interface, OpenVAF will generate a more
* optimized implementation.
*
*/
#include "osdi.h"
#include "string.h"
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
// public interface
extern uint32_t OSDI_VERSION_MAJOR;
extern uint32_t OSDI_VERSION_MINOR;
extern uint32_t OSDI_NUM_DESCRIPTORS;
extern OsdiDescriptor OSDI_DESCRIPTORS[1];
// number of nodes and definitions of node ids for nicer syntax in this file
// note: order should be same as "nodes" list defined later
#define NUM_NODES 3
#define P 0
#define M 1
// number of matrix entries and definitions for Jacobian entries for nicer
// syntax in this file
#define NUM_MATRIX 4
#define P_P 0
#define P_M 1
#define M_P 2
#define M_M 3
// The model structure for the diode
typedef struct CapacitorModel
{
double C;
bool C_given;
} CapacitorModel;
// The instace structure for the diode
typedef struct CapacitorInstance
{
double temperature;
double rhs_resist[NUM_NODES];
double rhs_react[NUM_NODES];
double jacobian_resist[NUM_MATRIX];
double jacobian_react[NUM_MATRIX];
double *jacobian_ptr_resist[NUM_MATRIX];
double *jacobian_ptr_react[NUM_MATRIX];
uint32_t node_off[NUM_NODES];
} CapacitorInstance;
// implementation of the access function as defined by the OSDI spec
void *osdi_access(void *inst_, void *model_, uint32_t id, uint32_t flags)
{
CapacitorModel *model = (CapacitorModel *)model_;
CapacitorInstance *inst = (CapacitorInstance *)inst_;
bool *given;
void *value;
switch (id) // id of params defined in param_opvar array
{
case 0:
value = (void *)&model->C;
given = &model->C_given;
break;
default:
return NULL;
}
if (flags & ACCESS_FLAG_SET)
{
*given = true;
}
return value;
}
// implementation of the setup_model function as defined in the OSDI spec
OsdiInitInfo setup_model(void *_handle, void *model_)
{
CapacitorModel *model = (CapacitorModel *)model_;
// set parameters and check bounds
if (!model->C_given)
{
model->C = 1e-15;
}
return (OsdiInitInfo){.flags = 0, .num_errors = 0, .errors = NULL};
}
// implementation of the setup_instace function as defined in the OSDI spec
OsdiInitInfo setup_instance(void *_handle, void *inst_, void *model_,
double temperature, uint32_t _num_terminals)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
CapacitorModel *model = (CapacitorModel *)model_;
inst->temperature = temperature;
return (OsdiInitInfo){.flags = 0, .num_errors = 0, .errors = NULL};
}
// implementation of the eval function as defined in the OSDI spec
uint32_t eval(void *handle, void *inst_, void *model_, uint32_t flags,
double *prev_solve, OsdiSimParas *sim_params)
{
CapacitorModel *model = (CapacitorModel *)model_;
CapacitorInstance *inst = (CapacitorInstance *)inst_;
// get voltages
double vp = prev_solve[inst->node_off[P]];
double vm = prev_solve[inst->node_off[M]];
double vpm = vp - vm;
double gmin = 1e-12;
for (int i = 0; sim_params->names[i] != NULL; i++)
{
if (strcmp(sim_params->names[i], "gmin") == 0)
{
gmin = sim_params->vals[i];
}
}
double qc_vpm = model->C;
double qc = model->C * vpm;
////////////////////////////////
// evaluate model equations
////////////////////////////////
if (flags & CALC_REACT_RESIDUAL)
{
// write react rhs
inst->rhs_react[P] = qc;
inst->rhs_react[M] = -qc;
}
//////////////////
// write Jacobian
//////////////////
if (flags & CALC_REACT_JACOBIAN)
{
// write react matrix
// stamp Qd between nodes A and Ci depending also on dT
inst->jacobian_react[P_P] = qc_vpm;
inst->jacobian_react[P_M] = -qc_vpm;
inst->jacobian_react[M_P] = -qc_vpm;
inst->jacobian_react[M_M] = qc_vpm;
}
return 0;
}
// TODO implementation of the load_noise function as defined in the OSDI spec
void load_noise(void *inst, void *model, double freq, double *noise_dens,
double *ln_noise_dens)
{
// TODO add noise to example
}
#define LOAD_RHS_RESIST(name) \
dst[inst->node_off[name]] += inst->rhs_resist[name];
// implementation of the load_rhs_resist function as defined in the OSDI spec
void load_residual_resist(void *inst_, double *dst)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
LOAD_RHS_RESIST(P)
LOAD_RHS_RESIST(M)
}
#define LOAD_RHS_REACT(name) dst[inst->node_off[name]] += inst->rhs_react[name];
// implementation of the load_rhs_react function as defined in the OSDI spec
void load_residual_react(void *inst_, double *dst)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
LOAD_RHS_REACT(P)
LOAD_RHS_REACT(M)
}
#define LOAD_MATRIX_RESIST(name) \
*inst->jacobian_ptr_resist[name] += inst->jacobian_resist[name];
// implementation of the load_matrix_resist function as defined in the OSDI spec
void load_jacobian_resist(void *inst_)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
LOAD_MATRIX_RESIST(P_P)
LOAD_MATRIX_RESIST(P_M)
LOAD_MATRIX_RESIST(M_P)
LOAD_MATRIX_RESIST(M_M)
}
#define LOAD_MATRIX_REACT(name) \
*inst->jacobian_ptr_react[name] += inst->jacobian_react[name] * alpha;
// implementation of the load_matrix_react function as defined in the OSDI spec
void load_jacobian_react(void *inst_, double alpha)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
LOAD_MATRIX_REACT(P_P)
LOAD_MATRIX_REACT(M_M)
LOAD_MATRIX_REACT(P_M)
LOAD_MATRIX_REACT(M_P)
}
#define LOAD_MATRIX_TRAN(name) \
*inst->jacobian_ptr_resist[name] += inst->jacobian_react[name] * alpha;
// implementation of the load_matrix_tran function as defined in the OSDI spec
void load_jacobian_tran(void *inst_, double alpha)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
// set dc stamps
load_jacobian_resist(inst_);
// add reactive contributions
LOAD_MATRIX_TRAN(P_P)
LOAD_MATRIX_TRAN(M_M)
LOAD_MATRIX_TRAN(M_P)
LOAD_MATRIX_TRAN(M_M)
}
// implementation of the load_spice_rhs_dc function as defined in the OSDI spec
void load_spice_rhs_dc(void *inst_, double *dst, double *prev_solve)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
double vp = prev_solve[inst->node_off[P]];
double vm = prev_solve[inst->node_off[M]];
dst[inst->node_off[P]] += inst->jacobian_resist[P_M] * vm +
inst->jacobian_resist[P_P] * vp -
inst->rhs_resist[P];
dst[inst->node_off[M]] += inst->jacobian_resist[M_P] * vp +
inst->jacobian_resist[M_M] * vm -
inst->rhs_resist[M];
}
// implementation of the load_spice_rhs_tran function as defined in the OSDI
// spec
void load_spice_rhs_tran(void *inst_, double *dst, double *prev_solve,
double alpha)
{
CapacitorInstance *inst = (CapacitorInstance *)inst_;
double vp = prev_solve[inst->node_off[P]];
double vm = prev_solve[inst->node_off[M]];
// set DC rhs
load_spice_rhs_dc(inst_, dst, prev_solve);
// add contributions due to reactive elements
dst[inst->node_off[P]] +=
alpha * (inst->jacobian_react[P_P] * vp +
inst->jacobian_react[P_M] * vm);
dst[inst->node_off[M]] += alpha * (inst->jacobian_react[M_M] * vm +
inst->jacobian_react[M_P] * vp);
}
// structure that provides information of all nodes of the model
OsdiNode nodes[NUM_NODES] = {
{.name = "P", .units = "V", .is_reactive = true},
{.name = "M", .units = "V", .is_reactive = true},
};
// boolean array that tells which Jacobian entries are constant. Nothing is
// constant with selfheating, though.
bool const_jacobian_entries[NUM_MATRIX] = {};
// these node pairs specify which entries in the Jacobian must be accounted for
OsdiNodePair jacobian_entries[NUM_MATRIX] = {
{P, P},
{P, M},
{M, P},
{M, M},
};
#define NUM_PARAMS 1
// the model parameters as defined in Verilog-A, bounds and default values are
// stored elsewhere as they may depend on model parameters etc.
OsdiParamOpvar params[NUM_PARAMS] = {
{
.name = (char *[]){"C"},
.num_alias = 0,
.description = "Capacitance",
.units = "Farad",
.flags = PARA_TY_REAL | PARA_KIND_MODEL,
.len = 0,
},
};
// fill exported data
uint32_t OSDI_VERSION_MAJOR = OSDI_VERSION_MAJOR_CURR;
uint32_t OSDI_VERSION_MINOR = OSDI_VERSION_MINOR_CURR;
uint32_t OSDI_NUM_DESCRIPTORS = 1;
// this is the main structure used by simulators, it gives access to all
// information in a model
OsdiDescriptor OSDI_DESCRIPTORS[1] = {{
// metadata
.name = "capacitor_va",
// nodes
.num_nodes = NUM_NODES,
.num_terminals = 2,
.nodes = (OsdiNode *)&nodes,
// matrix entries
.num_jacobian_entries = NUM_MATRIX,
.jacobian_entries = (OsdiNodePair *)&jacobian_entries,
.const_jacobian_entries = (bool *)&const_jacobian_entries,
// memory
.instance_size = sizeof(CapacitorInstance),
.model_size = sizeof(CapacitorModel),
.residual_resist_offset = offsetof(CapacitorInstance, rhs_resist),
.residual_react_offset = offsetof(CapacitorInstance, rhs_react),
.node_mapping_offset = offsetof(CapacitorInstance, node_off),
.jacobian_resist_offset = offsetof(CapacitorInstance, jacobian_resist),
.jacobian_react_offset = offsetof(CapacitorInstance, jacobian_react),
.jacobian_ptr_resist_offset = offsetof(CapacitorInstance, jacobian_ptr_resist),
.jacobian_ptr_react_offset = offsetof(CapacitorInstance, jacobian_ptr_react),
// TODO add node collapsing to example
// node collapsing
.num_collapsible = 0,
.collapsible = NULL,
.is_collapsible_offset = 0,
// noise
.noise_sources = NULL,
.num_noise_src = 0,
// parameters and op vars
.num_params = NUM_PARAMS,
.num_instance_params = 0,
.num_opvars = 0,
.param_opvar = (OsdiParamOpvar *)&params,
// setup
.access = &osdi_access,
.setup_model = &setup_model,
.setup_instance = &setup_instance,
.eval = &eval,
.load_noise = &load_noise,
.load_residual_resist = &load_residual_resist,
.load_residual_react = &load_residual_react,
.load_spice_rhs_dc = &load_spice_rhs_dc,
.load_spice_rhs_tran = &load_spice_rhs_tran,
.load_jacobian_resist = &load_jacobian_resist,
.load_jacobian_react = &load_jacobian_react,
.load_jacobian_tran = &load_jacobian_tran,
}};

View File

@ -7,7 +7,7 @@ VD Dx 0 DC 0 AC 1 SIN (0.5 0.2 1M)
Vsense Dx D DC 0
* model definitions:
.model cmod_osdi osdi capacitor_va c=5e-12
.model cmod_osdi capacitor_va c=5e-12
*OSDI Capacitor:
*OSDI_ACTIVATE*A1 D 0 cmod_osdi
@ -17,6 +17,7 @@ Vsense Dx D DC 0
.control
pre_osdi capacitor.osdi
set filetype=ascii
set wr_vecnames
set wr_singlescale

View File

@ -2,10 +2,12 @@
"""
import os, shutil
import numpy as np
import subprocess
import pandas as pd
import sys
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
directory = os.path.dirname(__file__)
from testing import prepare_test
# This test runs a DC, AC and Transient Simulation of a simple capacitor.
# The capacitor is available as a C file and needs to be compiled to a shared object
@ -17,78 +19,12 @@ directory = os.path.dirname(__file__)
# The integration test proves the functioning of the OSDI interface.
# Future tests will target Verilog-A models like HICUM/L2 that should yield exactly the same results as the Ngspice implementation.
def create_shared_object():
# place the file "capacitor_va.c" next to this file
subprocess.run(
[
"gcc",
"-c",
"-Wall",
"-I",
"../../src/spicelib/devices/osdi/",
"-fpic",
"capacitor_va.c",
"-ggdb",
],
cwd=directory,
)
subprocess.run(
["gcc", "-shared", "-o", "capacitor_va.so", "capacitor_va.o", "-ggdb"],
cwd=directory,
)
os.makedirs(os.path.join(directory, "test_osdi", "osdi"), exist_ok=True)
subprocess.run(
["mv", "capacitor_va.so", "test_osdi/osdi/capacitor_va.so"], cwd=directory
)
subprocess.run(["rm", "capacitor_va.o"], cwd=directory)
# specify location of Ngspice executable to be tested
ngspice_path = os.path.join(directory, "../../debug/src/ngspice")
ngspice_path = os.path.abspath(ngspice_path)
directory = os.path.dirname(__file__)
def test_ngspice():
path_netlist = os.path.join(directory, "netlist.sp")
# open netlist and activate Ngspice capacitor
with open(path_netlist) as netlist_handle:
netlist_raw = netlist_handle.read()
netlist_osdi = netlist_raw.replace("*OSDI_ACTIVATE*", "")
netlist_built_in = netlist_raw.replace("*BUILT_IN_ACTIVATE*", "")
# make directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
# remove old results:
for directory_i in [dir_osdi, dir_built_in]:
shutil.rmtree(directory_i, ignore_errors=True)
for directory_i in [dir_osdi, dir_built_in]:
os.makedirs(directory_i, exist_ok=True)
create_shared_object()
# write netlists
with open(os.path.join(dir_osdi, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_osdi)
with open(os.path.join(dir_built_in, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_built_in)
# run simulations with Ngspice
for directory_i in [dir_osdi, dir_built_in]:
subprocess.run(
[
ngspice_path,
"netlist.sp",
"-b",
],
cwd=directory_i,
)
dir_osdi, dir_built_in = prepare_test(directory)
# read DC simulation results
dc_data_osdi = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")
dc_data_built_in = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")

View File

@ -2,10 +2,12 @@
"""
import os, shutil
import numpy as np
import subprocess
import pandas as pd
import sys
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
directory = os.path.dirname(__file__)
from testing import prepare_test
# This test runs a DC, AC and Transient Simulation of a simple diode.
# The diode is available in the "OSDI" Git project and needs to be compiled to a shared object
@ -18,74 +20,11 @@ directory = os.path.dirname(__file__)
# complicated and the results are therefore not exactly the same.
# Future tests will target Verilog-A models like HICUM/L2 that should yield exactly the same results as the Ngspice implementation.
def create_shared_object():
# place the file "diode.c" next to this file
subprocess.run(
[
"gcc",
"-c",
"-Wall",
"-I",
"../../src/osdi/",
"-fpic",
"diode.c",
"-ggdb",
],
cwd=directory,
)
subprocess.run(
["gcc", "-shared", "-o", "diode.osdi", "diode.o", "-ggdb"],
cwd=directory,
)
subprocess.run(["mv", "diode.osdi", "test_osdi/diode.osdi"], cwd=directory)
subprocess.run(["rm", "diode.o"], cwd=directory)
# specify location of Ngspice executable to be tested
ngspice_path = os.path.join(directory, "../../debug/src/ngspice")
ngspice_path = os.path.abspath(ngspice_path)
directory = os.path.dirname(__file__)
def test_ngspice():
path_netlist = os.path.join(directory, "netlist.sp")
# open netlist and activate Ngspice diode
with open(path_netlist) as netlist_handle:
netlist_raw = netlist_handle.read()
netlist_osdi = netlist_raw.replace("*OSDI_ACTIVATE*", "")
netlist_built_in = netlist_raw.replace("*BUILT_IN_ACTIVATE*", "")
# make directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
# remove old results:
for directory_i in [dir_osdi, dir_built_in]:
shutil.rmtree(directory_i, ignore_errors=True)
for directory_i in [dir_osdi, dir_built_in]:
os.makedirs(directory_i, exist_ok=True)
create_shared_object()
# write netlists
with open(os.path.join(dir_osdi, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_osdi)
with open(os.path.join(dir_built_in, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_built_in)
# run simulations with Ngspice
for directory_i in [dir_osdi, dir_built_in]:
subprocess.run(
[
ngspice_path,
"netlist.sp",
"-b",
],
cwd=directory_i,
)
dir_osdi, dir_built_in = prepare_test(directory)
# read DC simulation results
dc_data_osdi = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")

Binary file not shown.

View File

@ -1,4 +1,4 @@
OSDI Resistor Test
OSDI Multiple Devices Test
.options abstol=1e-15
@ -7,15 +7,17 @@ VD Dx 0 DC 0 AC 1 SIN (0.5 0.2 1M)
Vsense Dx D DC 0
* model definitions:
.model rmod_osdi resistor_va r=10
.model rmod_osdi resistor_va r=20
.model cmod_osdi capacitor_va r=5e-12
*OSDI Resistor and Capacitor:
*OSDI_ACTIVATE*A1 D 0 rmod_osdi
*OSDI_ACTIVATE*A2 D 0 cmod_osdi
*OSDI_ACTIVATE*A2 D 0 rmod_osdi
*OSDI_ACTIVATE*A3 D 0 cmod_osdi
*Built-in Capacitor and Resistor:
*BUILT_IN_ACTIVATE*R1 D 0 10
*BUILT_IN_ACTIVATE*R1 D 0 20
*BUILT_IN_ACTIVATE*R2 D 0 20
*BUILT_IN_ACTIVATE*C1 D 0 5e-12

View File

@ -2,10 +2,12 @@
"""
import os, shutil
import numpy as np
import subprocess
import pandas as pd
import sys
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
directory = os.path.dirname(__file__)
from testing import prepare_test
# This test runs a DC, AC and Transient Simulation of a simple resistor and resistor.
# The capacitor and resistor are available as C files and need to be compiled to a shared objects
@ -20,101 +22,31 @@ directory = os.path.dirname(__file__)
# Future tests will target Verilog-A models like HICUM/L2 that should yield exactly the same results as the Ngspice implementation.
def create_shared_object():
# place the file "resistor.c" and "capacitor.c" next to this file
for dev in ["capacitor", "resistor"]:
subprocess.run(
[
"gcc",
"-c",
"-Wall",
"-I",
"../../src/osdi/",
"-fpic",
dev + ".c",
"-ggdb",
],
cwd=directory,
)
subprocess.run(
["gcc", "-shared", "-o", dev + ".osdi", dev + ".o", "-ggdb"],
cwd=directory,
)
subprocess.run(
["mv", dev + ".osdi", "test_osdi/" + dev + ".osdi"], cwd=directory
)
subprocess.run(["rm", dev + ".o"], cwd=directory)
# specify location of Ngspice executable to be tested
ngspice_path = os.path.join(directory, "../../debug/src/ngspice")
ngspice_path = os.path.abspath(ngspice_path)
directory = os.path.dirname(__file__)
def test_ngspice():
path_netlist = os.path.join(directory, "netlist.sp")
# open netlist and activate Ngspice devices
with open(path_netlist) as netlist_handle:
netlist_raw = netlist_handle.read()
netlist_osdi = netlist_raw.replace("*OSDI_ACTIVATE*", "")
netlist_built_in = netlist_raw.replace("*BUILT_IN_ACTIVATE*", "")
# make directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
# remove old results:
for directory_i in [dir_osdi, dir_built_in]:
shutil.rmtree(directory_i, ignore_errors=True)
for directory_i in [dir_osdi, dir_built_in]:
os.makedirs(directory_i, exist_ok=True)
create_shared_object()
# write netlists
with open(os.path.join(dir_osdi, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_osdi)
with open(os.path.join(dir_built_in, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_built_in)
# run simulations with Ngspice
for directory_i in [dir_osdi, dir_built_in]:
subprocess.run(
[
ngspice_path,
"netlist.sp",
"-b",
],
cwd=directory_i,
)
dir_osdi, dir_built_in = prepare_test(directory)
# read DC simulation results
dc_data_osdi = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")
dc_data_built_in = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")
# dc_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "dc_sim.ngspice"), sep="\\s+"
# )
dc_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "dc_sim.ngspice"), sep="\\s+"
)
id_osdi = dc_data_osdi["i(vsense)"].to_numpy()
id_built_in = dc_data_osdi["i(vsense)"].to_numpy()
# id_built_in = dc_data_built_in["i(vsense)"].to_numpy()
id_built_in = dc_data_built_in["i(vsense)"].to_numpy()
# read AC simulation results
ac_data_osdi = pd.read_csv(os.path.join(dir_osdi, "ac_sim.ngspice"), sep="\\s+")
ac_data_built_in = pd.read_csv(os.path.join(dir_osdi, "ac_sim.ngspice"), sep="\\s+")
# ac_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "ac_sim.ngspice"), sep="\\s+"
# )
ac_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "ac_sim.ngspice"), sep="\\s+"
)
# read TR simulation results
tr_data_osdi = pd.read_csv(os.path.join(dir_osdi, "tr_sim.ngspice"), sep="\\s+")
tr_data_built_in = pd.read_csv(os.path.join(dir_osdi, "tr_sim.ngspice"), sep="\\s+")
# tr_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "tr_sim.ngspice"), sep="\\s+"
# )
tr_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "tr_sim.ngspice"), sep="\\s+"
)
# test simulation results
id_osdi = dc_data_osdi["i(vsense)"].to_numpy()

View File

@ -7,7 +7,7 @@ VD Dx 0 DC 0 AC 1 SIN (0.5 0.2 1M)
Vsense Dx D DC 0
* model definitions:
.model rmod_osdi osdi resistor_va r=10
.model rmod_osdi resistor_va r=10
*OSDI Resistor:
*OSDI_ACTIVATE*A1 D 0 rmod_osdi
@ -17,6 +17,8 @@ Vsense Dx D DC 0
.control
pre_osdi resistor.osdi
set filetype=ascii
set wr_vecnames
set wr_singlescale

View File

@ -5,7 +5,12 @@ import numpy as np
import subprocess
import pandas as pd
directory = os.path.dirname(__file__)
import sys
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
from testing import prepare_test
# This test runs a DC, AC and Transient Simulation of a simple resistor.
# The capacitor is available as a C file and needs to be compiled to a shared object
@ -17,102 +22,32 @@ directory = os.path.dirname(__file__)
# The integration test proves the functioning of the OSDI interface.
# Future tests will target Verilog-A models like HICUM/L2 that should yield exactly the same results as the Ngspice implementation.
def create_shared_object():
# place the file "resistor_va.c" next to this file
subprocess.run(
[
"gcc",
"-c",
"-Wall",
"-I",
"../../src/spicelib/devices/osdi/",
"-fpic",
"resistor_va.c",
"-ggdb",
],
cwd=directory,
)
subprocess.run(
["gcc", "-shared", "-o", "resistor_va.so", "resistor_va.o", "-ggdb"],
cwd=directory,
)
os.makedirs(os.path.join(directory, "test_osdi", "osdi"), exist_ok=True)
subprocess.run(
["mv", "resistor_va.so", "test_osdi/osdi/resistor_va.so"], cwd=directory
)
subprocess.run(["rm", "resistor_va.o"], cwd=directory)
# specify location of Ngspice executable to be tested
ngspice_path = os.path.join(directory, "../../debug/src/ngspice")
ngspice_path = os.path.abspath(ngspice_path)
directory = os.path.dirname(__file__)
def test_ngspice():
path_netlist = os.path.join(directory, "netlist.sp")
# open netlist and activate Ngspice resistor
with open(path_netlist) as netlist_handle:
netlist_raw = netlist_handle.read()
netlist_osdi = netlist_raw.replace("*OSDI_ACTIVATE*", "")
netlist_built_in = netlist_raw.replace("*BUILT_IN_ACTIVATE*", "")
# make directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
# remove old results:
for directory_i in [dir_osdi, dir_built_in]:
shutil.rmtree(directory_i, ignore_errors=True)
for directory_i in [dir_osdi, dir_built_in]:
os.makedirs(directory_i, exist_ok=True)
create_shared_object()
# write netlists
with open(os.path.join(dir_osdi, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_osdi)
with open(os.path.join(dir_built_in, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_built_in)
# run simulations with Ngspice
for directory_i in [dir_osdi, dir_built_in]:
subprocess.run(
[
ngspice_path,
"netlist.sp",
"-b",
],
cwd=directory_i,
)
dir_osdi, dir_built_in = prepare_test(directory)
# read DC simulation results
dc_data_osdi = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")
dc_data_built_in = pd.read_csv(os.path.join(dir_osdi, "dc_sim.ngspice"), sep="\\s+")
# dc_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "dc_sim.ngspice"), sep="\\s+"
# )
dc_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "dc_sim.ngspice"), sep="\\s+"
)
id_osdi = dc_data_osdi["i(vsense)"].to_numpy()
id_built_in = dc_data_osdi["i(vsense)"].to_numpy()
# id_built_in = dc_data_built_in["i(vsense)"].to_numpy()
id_built_in = dc_data_built_in["i(vsense)"].to_numpy()
# read AC simulation results
ac_data_osdi = pd.read_csv(os.path.join(dir_osdi, "ac_sim.ngspice"), sep="\\s+")
ac_data_built_in = pd.read_csv(os.path.join(dir_osdi, "ac_sim.ngspice"), sep="\\s+")
# ac_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "ac_sim.ngspice"), sep="\\s+"
# )
ac_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "ac_sim.ngspice"), sep="\\s+"
)
# read TR simulation results
tr_data_osdi = pd.read_csv(os.path.join(dir_osdi, "tr_sim.ngspice"), sep="\\s+")
tr_data_built_in = pd.read_csv(os.path.join(dir_osdi, "tr_sim.ngspice"), sep="\\s+")
# tr_data_built_in = pd.read_csv(
# os.path.join(dir_built_in, "tr_sim.ngspice"), sep="\\s+"
# )
tr_data_built_in = pd.read_csv(
os.path.join(dir_built_in, "tr_sim.ngspice"), sep="\\s+"
)
# test simulation results
id_osdi = dc_data_osdi["i(vsense)"].to_numpy()

94
test_cases/testing.py Normal file
View File

@ -0,0 +1,94 @@
#this file defines some common routines used by the OSDI test cases
import subprocess
import os
import shutil
import glob
from pathlib import Path
# specify location of Ngspice executable to be tested
directory_testing = os.path.dirname(__file__)
ngspice_path = os.path.join(directory_testing, "../debug/src/ngspice")
ngspice_path = os.path.abspath(ngspice_path)
def create_shared_objects(directory):
c_files = []
for c_file in glob.glob(directory + "/*.c"):
basename = Path(c_file).stem
c_files.append(basename)
for c_file in c_files:
subprocess.run(
[
"gcc",
"-c",
"-Wall",
"-I",
"../../src/osdi/",
"-fpic",
c_file + ".c",
"-ggdb",
],
cwd=directory,
)
subprocess.run(
["gcc", "-shared", "-o", c_file + ".osdi", c_file + ".o", "-ggdb"],
cwd=directory,
)
subprocess.run(
["mv", c_file + ".osdi", "test_osdi/" + c_file + ".osdi"], cwd=directory
)
subprocess.run(["rm", c_file + ".o"], cwd=directory)
def prepare_dirs(directory):
# directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
for directory_i in [dir_osdi, dir_built_in]:
# remove old results
shutil.rmtree(directory_i, ignore_errors=True)
# make new directories
os.makedirs(directory_i, exist_ok=True)
return dir_osdi, dir_built_in
def prepare_netlists(directory):
path_netlist = os.path.join(directory, "netlist.sp")
# directories for test cases
dir_osdi = os.path.join(directory, "test_osdi")
dir_built_in = os.path.join(directory, "test_built_in")
# open netlist and activate Ngspice devices
with open(path_netlist) as netlist_handle:
netlist_raw = netlist_handle.read()
netlist_osdi = netlist_raw.replace("*OSDI_ACTIVATE*", "")
netlist_built_in = netlist_raw.replace("*BUILT_IN_ACTIVATE*", "")
# write netlists
with open(os.path.join(dir_osdi, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_osdi)
with open(os.path.join(dir_built_in, "netlist.sp"), "w") as netlist_handle:
netlist_handle.write(netlist_built_in)
def run_simulations(dirs):
for dir_i in dirs:
subprocess.run(
[
ngspice_path,
"netlist.sp",
"-b",
],
cwd=dir_i,
)
def prepare_test(directory):
dir_osdi, dir_built_in = prepare_dirs(directory)
create_shared_objects(directory)
prepare_netlists(directory)
run_simulations([dir_osdi, dir_built_in])
return dir_osdi, dir_built_in