Copyright Holger Vogt 2021-2024
License: BSD 3-clause
*/
#include "ngspice/ngspice.h"
#include "ngspice/cpextern.h"
#include "ngspice/dstring.h"
#include "numparam/general.h"
#include "ngspice/hash.h"
#include "ngspice/inpdefs.h"
#include "ngspice/wordlist.h"
#include "ngspice/stringskip.h"
void inp_probe(struct card* card);
void modprobenames(INPtables* tab);
extern struct card* insert_new_line(
struct card* card, char* line, int linenum, int linenum_orig, char *lineinfo);
extern int get_number_terminals(char* c);
extern char* search_plain_identifier(char* str, const char* identifier);
static char* get_terminal_name(char* element, char* numberstr, NGHASHPTR instances);
static char* get_terminal_number(char* element, char* numberstr);
static int setallvsources(struct card* tmpcard, NGHASHPTR instances, char* instname, int numnodes, bool haveall, bool power);
static int check_for_nodes(char* instance, int numnodes);
<empty> add V(0) current measure sources to all device nodes in addition to .save all
alli add V(0) current measure sources to all device nodes in addition to .save all
I(R1) add V(0) measure source to node 1 of a two-terminal device R1
I(Q1) add V(0) measure sources to all nodes of a multi-terminal device Q1
I(M4,3) add V(0) measure source to node 3 of a multi-terminal device M4
Vd(R1) add E source inputs to measure voltage difference to both terminals of a two-terminal device R1
Vd(X1:2:3) add E source inputs to terminals 2 and 3 of a multi-terminal device X1
Vd(X1:2,X2:3) add E source inputs to terminal 2 of a multi-terminal device X1 and to terminal 3 of X2
Seach the netlist for the devices found in the .probe parameters.
Check the number of terminals for each device. Add 0V voltage sources
in series to each of the named terminals. Add E sources to the differential
voltage probes.
*/
void inp_probe(struct card* deck)
{
struct card *card;
int skip_control = 0;
int skip_subckt = 0;
wordlist* probes = NULL, *probeparams = NULL, *wltmp, *allsaves = NULL;
bool haveall = FALSE, havedifferential = FALSE, t = TRUE, havesave = FALSE;
NGHASHPTR instances;
int ee = 0;
for (card = deck; card; card = card->nextcard) {
if (ciprefix(".probe", card->line)) {
probes = wl_cons(card->line, probes);
*(card->line) = '*';
}
}
if (probes == NULL)
return;
If not found, add '.save all' */
for (card = deck; card; card = card->nextcard) {
if (ciprefix(".save", card->line)) {
havesave = TRUE;
break;
}
else if (ciprefix(".control", card->line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", card->line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
if (ciprefix("save ", card->line)) {
havesave = TRUE;
break;
}
}
}
skip_control = 0;
if (!havesave) {
char* vline = copy(".save all");
deck = insert_new_line(deck, vline, 0, deck->linenum_orig, deck->linesource);
}
cp_vset("probe_is_given", CP_BOOL, &t);
for (wltmp = probes; wltmp; wltmp = wltmp->wl_next) {
char* nextnode;
char* tmpstr = wltmp->wl_word;
tmpstr = nexttok(tmpstr);
if (*tmpstr == '\0') {
fprintf(stderr, "Note: Empty .probe command, treated as .probe alli\n");
haveall = TRUE;
continue;
}
char *allistr = search_plain_identifier(tmpstr, "alli");
if (allistr) {
haveall = TRUE;
memcpy(allistr, " ", 4);
}
tmpstr = skip_ws(tmpstr);
if (!strchr("vipVIP", *tmpstr)) {
fprintf(stderr, "Warning: Strange parameter in line %s, ingnored\n", wltmp->wl_word);
tmpstr = nexttok(tmpstr);
}
nextnode = gettok_char(&tmpstr, ')', TRUE, FALSE);
if (haveall == FALSE && !nextnode) {
fprintf(stderr, "Warning: Strange parameter in line %s, ingnored\n", wltmp->wl_word);
continue;
}
while (nextnode && (*nextnode != '\0')) {
probeparams = wl_cons(nextnode, probeparams);
if (ciprefix("vd(", nextnode)) {
havedifferential = TRUE;
}
nextnode = gettok_char(&tmpstr, ')', TRUE, FALSE);
}
}
tfree(probes);
is the storage location of the card) */
instances = nghash_init(100);
nghash_unique(instances, TRUE);
for (card = deck; card; card = card->nextcard) {
char* curr_line = card->line;
if (ciprefix(".control", curr_line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", curr_line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
if (ciprefix(".subckt", curr_line)) {
skip_subckt++;
continue;
}
else if (ciprefix(".ends", curr_line)) {
skip_subckt--;
continue;
}
else if (skip_subckt > 0) {
continue;
}
if (*curr_line == '*')
continue;
if (*curr_line == '.')
continue;
if (*curr_line == '\0')
continue;
Put all instance names as key into a hash table, with the address as parameter. */
char* instname = gettok_instance(&curr_line);
if (!instname)
continue;
nghash_insert(instances, instname, card);
}
if (haveall || probeparams == NULL) {
Add current measure voltage sources for all devices, add differential E sources only for selected devices. */
int numnodes, i;
for (card = deck; card; card = card->nextcard) {
char* curr_line = card->line;
struct card* prevcard = NULL;
int nn = 0;
if (ciprefix(".control", curr_line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", curr_line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
if (ciprefix(".subckt", curr_line)) {
skip_subckt++;
continue;
}
else if (ciprefix(".ends", curr_line)) {
skip_subckt--;
continue;
}
else if (skip_subckt > 0) {
continue;
}
if (*curr_line == '*')
continue;
if (*curr_line == '.')
continue;
if (*curr_line == '\0')
continue;
char* instname = gettok_instance(&curr_line);
if (!instname)
continue;
if (strchr("ehvk", *instname))
continue;
digital nodes should not get V sources in series anyway.) */
if ('a' == *instname)
continue;
and digital nodes should not get V sources in series anyway.),
when probe_alli_nox is set in .spiceinit. */
if ('x' == *instname && cp_getvar("probe_alli_nox", CP_BOOL, NULL, 0))
continue;
We have three or four tokens until model name, but only the first 2 are relevant nodes. */
if (strchr("fgsw", *instname))
numnodes = 2;
else
numnodes = get_number_terminals(card->line);
if (check_for_nodes(card->line, numnodes)) {
fprintf(stderr, "Error: Not enough tokens in line %d\n%s\n", card->linenum_orig, card->line);
fprintf(stderr, " Please correct your input file\n");
controlled_exit(EXIT_BAD);
}
char* thisline = curr_line;
prevcard = card;
if (numnodes == 2) {
char *strnode1, *strnode2, *nodename2;
strnode1 = gettok(&thisline);
strnode2 = gettok(&thisline);
if (!strnode2 || *strnode2 == '\0') {
fprintf(stderr, "Warning: Cannot read 2 nodes in line %s\n", curr_line);
fprintf(stderr, " Instance not ready for .probe command\n");
tfree(strnode1);
tfree(strnode2);
continue;
}
nodename2 = get_terminal_name(instname, "2", instances);
char* newnode = tprintf("probe_int_%s_%s", strnode2, instname);
char* vline = tprintf("vcurr_%s:%s_%s %s %s 0", instname, nodename2, strnode2, newnode, strnode2);
char *newline = tprintf("%s %s %s %s", instname, strnode1, newnode, thisline);
char* nodesaves = tprintf("%s#branch", instname);
allsaves = wl_cons(nodesaves, allsaves);
tfree(card->line);
card->line = newline;
card = insert_new_line(card, vline, 0, card->linenum_orig, card->linesource);
tfree(strnode1);
tfree(strnode2);
tfree(newnode);
tfree(nodename2);
}
else {
char* nodename;
DS_CREATE(dnewline, 200);
sadd(&dnewline, instname);
cadd(&dnewline, ' ');
for (i = 1; i <= numnodes; i++) {
char* thisnode;
char nodebuf[20];
thisnode = gettok(&thisline);
if (!thisnode || *thisnode == '\0') {
fprintf(stderr, "Warning: Cannot read node %d in line %s\n", i, curr_line);
fprintf(stderr, " Instance not ready for .probe command\n");
tfree(thisnode);
continue;
}
char* newnode = tprintf("probe_int_%s_%s_%d", thisnode, instname, i);
sadd(&dnewline, newnode);
cadd(&dnewline, ' ');
snprintf(nodebuf, 12, "%d", i);
nodename = get_terminal_name(instname, nodebuf, instances);
if (!nodename || *nodename == '\0') {
fprintf(stderr, "Warning: Cannot find node name %d in line %s\n", i, curr_line);
fprintf(stderr, " Instance not ready for .probe command\n");
tfree(thisnode);
continue;
}
char* vline = tprintf("vcurr_%s:%s:%s_%s %s %s 0", instname, nodename, thisnode, nodebuf, thisnode, newnode);
card = insert_new_line(card, vline, 0, card->linenum_orig, card->linesource);
if (*instname == 'x' && strstr(thisnode, "unconnected")) {
char *rline = tprintf("R%s%d %s 0 1e15", thisnode, nn++, thisnode);
card = insert_new_line(card, rline, 0, card->linenum_orig, card->linesource);
}
char* nodesaves = tprintf("%s:%s#branch", instname, nodename);
allsaves = wl_cons(nodesaves, allsaves);
tfree(newnode);
tfree(nodename);
}
sadd(&dnewline, thisline);
tfree(prevcard->line);
prevcard->line = copy(ds_get_buf(&dnewline));
ds_free(&dnewline);
}
if (allsaves) {
allsaves = wl_cons(copy(".save"), allsaves);
char* newline = wl_flatten(allsaves);
wl_free(allsaves);
allsaves = NULL;
card = insert_new_line(card, newline, 0, card->linenum_orig, card->linesource);
}
}
}
if (probeparams) {
Add current measure voltage sources only for the selected devices.
Add differential probes only if 'all' had been found. */
for (wltmp = probeparams; wltmp; wltmp = wltmp->wl_next) {
char *tmpstr = wltmp->wl_word;
ee++;
v(nR1) voltage at node named nR1
vd(R1) voltage across a two-terminal device named R1
vd(m4:1:0) voltage at instance node 1 of device m4
vd(m4:1:3) voltage between instance nodes 1 and 3 of device m4
vd(m4:1, m5:3) voltage between instance node 1 of device m4 and node 3 of device m5 */
if (ciprefix("v(", tmpstr)) {
char* instname1 = gettok_char(&tmpstr, ')', TRUE, FALSE);
allsaves = wl_cons(copy(instname1), allsaves);
continue;
}
else if (ciprefix("vd(", tmpstr)) {
char* instname1, *instname2;
int numnodes1, numnodes2;
struct card* tmpcard1;
tmpstr += 3;
vd(nodename1,nodename2) */
if (!strchr(tmpstr, ':')) {
char* newline = NULL, *strnode1, *strnode2, *tmpstr2;
tmpstr2 = tmpstr;
strnode1 = gettok_char(&tmpstr2, ',', FALSE, FALSE);
if (strnode1) {
tmpstr2++;
strnode2 = gettok_char(&tmpstr2, ')', FALSE, FALSE);
if (!strnode2) {
}
else {
newline = tprintf("Ediff%d_nodes vd_%s:%s 0 %s %s 1", ee, strnode1, strnode2, strnode1, strnode2);
char* nodesaves = tprintf("vd_%s:%s", strnode1, strnode2);
allsaves = wl_cons(nodesaves, allsaves);
tfree(strnode1);
tfree(strnode2);
tmpcard1 = deck->nextcard;
tmpcard1 = insert_new_line(tmpcard1, newline, 0, tmpcard1->linenum_orig, tmpcard1->linesource);
}
continue;
}
instname1 = gettok_char(&tmpstr, ')', FALSE, FALSE);
tmpcard1 = nghash_find(instances, instname1);
if (!tmpcard1) {
fprintf(stderr, "Warning: Could not find the instance line for %s,\n .probe %s will be ignored\n", instname1, wltmp->wl_word);
tfree(instname1);
continue;
}
char* thisline = tmpcard1->line;
numnodes1 = get_number_terminals(thisline);
if (numnodes1 != 2) {
fprintf(stderr, "Warning: Instance %s has more than 2 nodes,\n .probe %s will be ignored\n", instname1, wltmp->wl_word);
tfree(instname1);
continue;
}
thisline = nexttok(thisline);
strnode1 = gettok(&thisline);
strnode2 = gettok(&thisline);
if (!strnode2 || *strnode2 == '\0') {
fprintf(stderr, "Warning: Cannot read 2 nodes in line %s\n", tmpcard1->line);
fprintf(stderr, " Instance not ready for .probe command\n");
tfree(strnode1);
tfree(strnode2);
continue;
}
newline = tprintf("Ediff%d_%s vd_%s 0 %s %s 1", ee, instname1, instname1, strnode1, strnode2);
char* nodesaves = tprintf("vd_%s", instname1);
allsaves = wl_cons(nodesaves, allsaves);
tfree(strnode1);
tfree(strnode2);
tmpcard1 = insert_new_line(tmpcard1, newline, 0, tmpcard1->linenum_orig, tmpcard1->linesource);
continue;
}
vd(R1:1,R2:2)
vd(M4:1:3)
vd(m5:d:s)*/
else {
char* tmpstr2, *nodename1, *nodename2;
struct card* tmpcard2;
tmpstr2 = tmpstr;
instname1 = gettok_char(&tmpstr, ':', FALSE, FALSE);
if (!instname1) {
fprintf(stderr, "Warning: Cannot read instance name in %s, ignored\n", tmpstr);
continue;
}
tmpcard1 = nghash_find(instances, instname1);
if (!tmpcard1) {
fprintf(stderr, "Warning: Could not find the instance line for %s,\n .probe %s will be ignored\n", instname1, wltmp->wl_word);
tfree(instname1);
continue;
}
char* thisline = tmpcard1->line;
numnodes1 = get_number_terminals(thisline);
tmpstr++;
tmpstr2 = tmpstr;
nodename1 = gettok_char(&tmpstr2, ',', FALSE, FALSE);
if (nodename1) {
int nodenum1, nodenum2, i;
char* ptr, *node1, *node2, *strnode1, *strnode2;
bool err = FALSE;
tmpstr2++;
instname2 = gettok_char(&tmpstr2, ':', FALSE, FALSE);
if (!instname2) {
fprintf(stderr, "Warning: Cannot read instance name in %s, ignored\n", tmpstr);
tfree(nodename1);
continue;
}
tmpstr2++;
tmpcard2 = nghash_find(instances, instname2);
if (!tmpcard2) {
fprintf(stderr, "Warning: Could not find the instance line for %s,\n .probe %s will be ignored\n", instname2, wltmp->wl_word);
tfree(instname2);
tfree(nodename1);
continue;
}
char* thisline2 = tmpcard2->line;
numnodes2 = get_number_terminals(thisline2);
nodename2 = gettok_char(&tmpstr2, ')', FALSE, FALSE);
if (!nodename2) {
fprintf(stderr, "Warning: Could not find the second node name for %s,\n .probe %s will be ignored\n", instname2, wltmp->wl_word);
tfree(instname2);
tfree(nodename1);
continue;
}
node1 = get_terminal_number(instname1, nodename1);
if (eq(node1, "0")) {
fprintf(stderr, "Warning: Node %s is not available for device %s,\n .probe %s will be ignored\n", node1, instname1, wltmp->wl_word);
continue;
}
node2 = get_terminal_number(instname2, nodename2);
if (eq(node2, "0")) {
fprintf(stderr, "Warning: Node %s is not available for device %s,\n .probe %s will be ignored\n", node1, instname2, wltmp->wl_word);
continue;
}
nodenum1 = (int)strtol(node1, &ptr, 10);
nodenum2 = (int)strtol(node2, &ptr, 10);
if (nodenum1 > numnodes1) {
fprintf(stderr, "Warning: There are only %d nodes available for %s,\n .probe %s will be ignored!\n", numnodes1, instname1, wltmp->wl_word);
continue;
}
if (nodenum2 > numnodes2) {
fprintf(stderr, "Warning: There are only %d nodes available for %s,\n .probe %s will be ignored!\n", numnodes2, instname2, wltmp->wl_word);
continue;
}
if (nodenum1 == nodenum2 && eq(instname1, instname2)) {
fprintf(stderr, "Warning: Duplicate node numbers and instances,\n .probe %s will be ignored!\n", wltmp->wl_word);
continue;
}
if (nodenum1 == 0) {
strnode1 = copy("0");
}
else {
for (i = 0; i < nodenum1; i++) {
thisline = nexttok(thisline);
if (*thisline == '\0') {
fprintf(stderr, "Warning: node number %d not available for instance %s, ignored!\n", nodenum1, instname1);
err = TRUE;
break;
}
}
if (err)
continue;
strnode1 = gettok(&thisline);
}
if (nodenum2 == 0) {
strnode2 = copy("0");
}
else {
for (i = 0; i < nodenum2; i++) {
thisline2 = nexttok(thisline2);
if (*thisline2 == '\0') {
fprintf(stderr, "Warning: node number %d not available for instance %s, ignored!\n", nodenum2, instname2);
err = TRUE;
break;
}
}
if (err)
continue;
strnode2 = gettok(&thisline2);
}
if (*node1 == '0') {
nodename1 = copy("0");
}
else {
if (*node1 != '\0' && atoi(node1) == 0) {
char* nn = get_terminal_number(instname1, node1);
tfree(node1);
node1 = copy(nn);
}
nodename1 = get_terminal_name(instname1, node1, instances);
}
if (*node2 == '0') {
nodename2 = copy("0");
}
else {
if (*node2 != '\0' && atoi(node2) == 0) {
char* nn = get_terminal_number(instname2, node2);
tfree(node2);
node2 = copy(nn);
}
nodename2 = get_terminal_name(instname2, node2, instances);
}
char *newline = tprintf("Ediff%d_%s_%s vd_%s:%s_%s:%s 0 %s %s 1", ee, instname1, instname2, instname1, nodename1, instname2, nodename2, strnode1, strnode2);
char* nodesaves = tprintf("vd_%s:%s_%s:%s", instname1, nodename1, instname2, nodename2);
allsaves = wl_cons(nodesaves, allsaves);
tmpcard1 = insert_new_line(tmpcard1, newline, 0, tmpcard1->linenum_orig, tmpcard1->linesource);
tfree(strnode1);
tfree(strnode2);
tfree(nodename1);
tfree(nodename2);
}
else {
int nodenum1, nodenum2, i;
char* ptr, * node1, * node2, * strnode1, * strnode2;
bool err = FALSE;
char* thisline2 = thisline;
tmpstr2 = tmpstr;
nodename1 = gettok_char(&tmpstr2, ':', FALSE, FALSE);
if (!nodename1) {
fprintf(stderr, "Warning: Could not find the first node name for %s,\n .probe %s will be ignored\n", instname1, wltmp->wl_word);
tfree(instname1);
continue;
}
tmpstr2++;
nodename2 = gettok_char(&tmpstr2, ')', FALSE, FALSE);
if (!nodename1 || !nodename2) {
fprintf(stderr, "Warning: Could not find the second node name for %s,\n .probe %s will be ignored\n", instname1, wltmp->wl_word);
tfree(instname1);
tfree(nodename1);
continue;
}
node1 = get_terminal_number(instname1, nodename1);
node2 = get_terminal_number(instname1, nodename2);
if (eq(node1, "0") && eq(node2, "0")) {
fprintf(stderr, "Warning: Either first or second node have to be non-zero,\n .probe %s will be ignored\n", wltmp->wl_word);
continue;
}
nodenum1 = (int)strtol(node1, &ptr, 10);
nodenum2 = (int)strtol(node2, &ptr, 10);
if (nodenum1 > numnodes1) {
fprintf(stderr, "Warning: There are only %d nodes available for %s,\n .probe %s will be ignored!\n", numnodes1, instname1, wltmp->wl_word);
continue;
}
if (nodenum2 > numnodes1) {
fprintf(stderr, "Warning: There are only %d nodes available for %s,\n .probe %s will be ignored!\n", numnodes1, instname1, wltmp->wl_word);
continue;
}
if (nodenum1 == nodenum2) {
fprintf(stderr, "Warning: Duplicate node numbers,\n .probe %s will be ignored!\n", wltmp->wl_word);
continue;
}
if (nodenum1 == 0) {
strnode1 = copy("0");
}
else {
for (i = 0; i < nodenum1; i++) {
thisline2 = nexttok(thisline2);
if (*thisline2 == '\0') {
fprintf(stderr, "Warning: node number %d not available for instance %s, ignored!\n", nodenum1, instname1);
err = TRUE;
break;
}
}
if (err)
continue;
strnode1 = gettok(&thisline2);
}
thisline2 = thisline;
if (nodenum2 == 0) {
strnode2 = copy("0");
}
else {
for (i = 0; i < nodenum2; i++) {
thisline2 = nexttok(thisline2);
if (*thisline2 == '\0') {
fprintf(stderr, "Warning: node number %d not available for instance %s, ignored!\n", nodenum2, instname1);
err = TRUE;
break;
}
}
if (err)
continue;
strnode2 = gettok(&thisline2);
}
if (*node1 == '0') {
nodename1 = copy("0");
}
else {
if (*node1 != '\0' && atoi(node1) == 0) {
char* nn = get_terminal_number(instname1, node1);
tfree(node1);
node1 = copy(nn);
}
nodename1 = get_terminal_name(instname1, node1, instances);
}
if (*node2 == '0') {
nodename2 = copy("0");
}
else {
if (*node2 != '\0' && atoi(node2) == 0) {
char* nn = get_terminal_number(instname1, node2);
tfree(node2);
node2 = copy(nn);
}
nodename2 = get_terminal_name(instname1, node2, instances);
}
char* newline = tprintf("Ediff%d_%s vd_%s:%s:%s 0 %s %s 1", ee, instname1, instname1, nodename1, nodename2, strnode1, strnode2);
char* nodesaves = tprintf("vd_%s:%s:%s", instname1, nodename1, nodename2);
allsaves = wl_cons(nodesaves, allsaves);
tmpcard1 = insert_new_line(tmpcard1, newline, 0, tmpcard1->linenum_orig, tmpcard1->linesource);
tfree(strnode1);
tfree(strnode2);
tfree(nodename1);
tfree(nodename2);
}
}
}
else if (!haveall && ciprefix("i(", tmpstr)) {
char* instname, * node1 = NULL, *nodename1;
struct card* tmpcard;
int numnodes;
tmpstr += 2;
char* co = strchr(tmpstr, ':');
if (co) {
*co = ',';
}
instname = gettok_noparens(&tmpstr);
tmpcard = nghash_find(instances, instname);
if (!tmpcard) {
fprintf(stderr, "Warning: Could not find the instance line for %s,\n .probe %s will be ignored\n", instname, wltmp->wl_word);
continue;
}
char* thisline = tmpcard->line;
We have three or four tokens until model name, but only the first 2 are relevant nodes. */
if (strchr("fgsw", *instname))
numnodes = 2;
else
numnodes = get_number_terminals(thisline);
if (check_for_nodes(tmpcard->line, numnodes)) {
fprintf(stderr, "Error: Not enough tokens in line %d\n%s\n", tmpcard->linenum_orig, tmpcard->line);
fprintf(stderr, " Please correct your input file\n");
controlled_exit(EXIT_BAD);
}
if (*tmpstr == ',')
tmpstr++;
node1 = gettok_noparens(&tmpstr);
if (*node1 != '\0' && atoi(node1) == 0) {
char *nn = get_terminal_number(instname, node1);
if (eq(nn, "0")) {
fprintf(stderr, "Warning: Node %s is not available for device %s,\n .probe %s will be ignored\n", node1, instname, wltmp->wl_word);
tfree(node1);
continue;
}
tfree(node1);
node1 = copy(nn);
}
if (node1 && *node1 == '\0') {
node1 = NULL;
nodename1 = copy("nn");
}
else
nodename1 = get_terminal_name(instname, node1, instances);
if (!node1 && numnodes == 2) {
char* newline, *strnode2, *nodename2;
thisline = nexttok(thisline);
thisline = nexttok(thisline);
char* begstr = copy_substring(tmpcard->line, thisline);
strnode2 = gettok(&thisline);
nodename2 = get_terminal_name(instname, "2", instances);
char* newnode = tprintf("probe_int_%s_%s_2", strnode2, instname);
char* vline = tprintf("vcurr_%s:%s_%s %s %s 0", instname, nodename2, strnode2, newnode, strnode2);
newline = tprintf("%s %s %s", begstr, newnode, thisline);
char* nodesaves = tprintf("%s#branch", instname);
allsaves = wl_cons(nodesaves, allsaves);
tfree(tmpcard->line);
tmpcard->line = newline;
tmpcard = insert_new_line(tmpcard, vline, 0, tmpcard->linenum_orig, tmpcard->linesource);
tfree(strnode2);
tfree(newnode);
tfree(begstr);
tfree(nodename1);
tfree(nodename2);
}
else if (!node1 && numnodes > 2) {
int err = 0;
tfree(nodename1);
err = setallvsources(tmpcard, instances, instname, numnodes, haveall, FALSE);
if (err) {
fprintf(stderr, "Warning: Cannot set zero voltage sources,\n .probe %s will be ignored\n", wltmp->wl_word);
}
continue;
}
else if (node1 && *node1 != '\0') {
char* newline, * ptr;
int nodenum;
int i;
bool err = FALSE;
nodenum = (int)strtol(node1, &ptr, 10);
if (nodenum > numnodes) {
fprintf(stderr, "Warning: There are only %d nodes available for %s,\n .probe %s will be ignored\n", numnodes, instname, wltmp->wl_word);
continue;
}
for (i = 0; i < nodenum; i++) {
thisline = nexttok(thisline);
if (*thisline == '\0') {
fprintf(stderr, "Warning: node number %d not available for instance %s!\n", nodenum, instname);
err = TRUE;
break;
}
}
if (err)
continue;
char* begstr = copy_substring(tmpcard->line, thisline);
char* strnode1 = gettok(&thisline);
char* newnode = tprintf("probe_int_%s_%s_%d", strnode1, instname, nodenum);
newline = tprintf("%s %s %s", begstr, newnode, thisline);
char* vline = tprintf("vcurr_%s:%s:%s_%s %s %s 0", instname, nodename1, node1, strnode1, strnode1, newnode);
tfree(tmpcard->line);
tmpcard->line = newline;
tmpcard = insert_new_line(tmpcard, vline, 0, tmpcard->linenum_orig, tmpcard->linesource);
char* nodesaves = tprintf("%s:%s#branch", instname, nodename1);
allsaves = wl_cons(nodesaves, allsaves);
tfree(begstr);
tfree(strnode1);
tfree(newnode);
tfree(nodename1);
}
}
else if (ciprefix("p(", tmpstr))
{
char* instname;
struct card* tmpcard;
int numnodes;
tmpstr += 2;
instname = gettok_noparens(&tmpstr);
tmpcard = nghash_find(instances, instname);
if (!tmpcard) {
fprintf(stderr, "Warning: Could not find the instance line for %s,\n .probe %s will be ignored\n", instname, wltmp->wl_word);
continue;
}
char* thisline = tmpcard->line;
We have three or four tokens until model name, but only the first 2 are relevant nodes. */
if (strchr("fgsw", *instname))
numnodes = 2;
else
numnodes = get_number_terminals(thisline);
if (numnodes < 2) {
fprintf(stderr, "Warning: Power mesasurement not available,\n .probe %s will be ignored\n", wltmp->wl_word);
tfree(instname);
continue;
}
if (check_for_nodes(tmpcard->line, numnodes)) {
fprintf(stderr, "Error: Not enough tokens in line %d\n%s\n", tmpcard->linenum_orig, tmpcard->line);
fprintf(stderr, " Please correct your input file\n");
controlled_exit(EXIT_BAD);
}
int err = 0;
err = setallvsources(tmpcard, instances, instname, numnodes, haveall, TRUE);
if (err == 1) {
fprintf(stderr, "Warning: Cannot set zero voltage sources,\n .probe %s will be ignored\n", wltmp->wl_word);
}
else if (err == 2) {
fprintf(stderr, "Warning: Zero voltage sources already set,\n .probe %s will be ignored\n", wltmp->wl_word);
}
else if (err == 3) {
fprintf(stderr, "Warning: Number of nodes mismatch,\n .probe %s will be ignored\n", wltmp->wl_word);
}
continue;
}
else if (!haveall) {
fprintf(stderr, "Warning: unknown .probe parameter %s,\n .probe %s will be ignored!\n", tmpstr, wltmp->wl_word);
continue;
}
}
if (allsaves) {
allsaves = wl_cons(copy(".save"), allsaves);
char* newline = wl_flatten(allsaves);
wl_free(allsaves);
allsaves = NULL;
card = deck->nextcard;
card = insert_new_line(card, newline, 0, card->linenum_orig, card->linesource);
}
}
nghash_free(instances, NULL, NULL);
}
get the node name, if defined (e.g. a for anode, c for cathode of a diode).
If not (yet) defined, return "nx" (x is the node number) or "nn" */
static char *get_terminal_name(char* element, char *numberstr, NGHASHPTR instances)
{
switch (*element) {
case 'r':
case 'c':
case 'l':
case 'k':
case 'f':
case 'h':
case 'b':
case 'v':
case 'i':
return tprintf("n%s", numberstr);
break;
case 'd':
switch (*numberstr) {
case 'a':
case '1':
return copy("a");
break;
case 'c':
case 'k':
case '2':
return copy("c");
break;
default:
return copy("nn");
break;
}
break;
case 'j':
case 'z':
switch (*numberstr) {
case 'd':
case '1':
return copy("d");
break;
case 'g':
case '2':
return copy("g");
break;
case 's':
case '3':
return copy("s");
break;
default:
return copy("nn");
break;
}
case 'm':
switch (*numberstr) {
case 'd':
case '1':
return copy("d");
break;
case 'g':
case '2':
return copy("g");
break;
case 's':
case '3':
return copy("s");
break;
case 'b':
case '4':
return copy("b_tj");
break;
case '5':
return copy("tc");
break;
case '6':
return copy("n6");
break;
case '7':
return copy("n7");
break;
default:
return copy("nn");
break;
}
case 'q':
switch (*numberstr) {
case 'c':
case '1':
return copy("c");
break;
case 'b':
case '2':
return copy("b");
break;
case 'e':
case '3':
return copy("e");
break;
case 's':
case '4':
return copy("s");
break;
case '5':
return copy("t");
break;
default:
return copy("nn");
break;
}
case 'x':
Get the subckt name from the x line
Search for the corresponding .subckt line
Find the numberstr node name of the .subckt
*/
{
int i;
char* subcktname, * ptr, * xcardsubsline = NULL, * subsnodestr;
struct card* xcard = nghash_find(instances, element);
char* thisline = xcard->line;
int numnodes = get_number_terminals(thisline);
int nodenumber = (int)strtol(numberstr, &ptr, 10);
for (i = 0; i <= numnodes; i++)
thisline = nexttok(thisline);
subcktname = gettok(&thisline);
struct card_assoc* allsubs = xcard->level->subckts;
if (!allsubs) {
char* instline = xcard->line;
char* inst = gettok(&instline);
fprintf(stderr, "Instance '%s' does not have an corresponding subcircuit '%s'!\n", inst, subcktname);
fprintf(stderr, " Is the model missing? .probe cannot determine subcircuit pin names.\n");
tfree(subcktname);
tfree(inst);
return tprintf("n%s", numberstr);
}
while (allsubs) {
xcardsubsline = allsubs->line->line;
if (!subcktname || !allsubs->name) {
tfree(subcktname);
return tprintf("n%s", numberstr);
}
if (cieq(subcktname, allsubs->name))
break;
allsubs = allsubs->next;
}
for (i = 1; i < nodenumber + 2; i++) {
xcardsubsline = nexttok(xcardsubsline);
}
subsnodestr = gettok(&xcardsubsline);
tfree(subcktname);
return subsnodestr;
break;
}
case 'u':
case 'w':
case 't':
case 'o':
case 'g':
case 'e':
case 's':
case 'y':
case 'p':
return tprintf("n%s", numberstr);
break;
default:
return copy("nn");
break;
}
}
if defined (e.g. a for anode, c for cathode of a diode)
return the node number. If there is no regular node
name, return "0". */
static char* get_terminal_number(char* element, char* namestr)
{
switch (*element) {
case 'r':
case 'c':
case 'l':
case 'k':
case 'f':
case 'h':
case 'b':
case 'v':
case 'i':
return "0";
break;
case 'd':
switch (*namestr) {
case 'a':
case '1':
return "1";
break;
case 'c':
case 'k':
case '2':
return "2";
break;
default:
return "0";
break;
}
break;
case 'j':
case 'z':
switch (*namestr) {
case 'd':
case '1':
return "1";
break;
case 'g':
case '2':
return "2";
break;
case 's':
case '3':
return "3";
break;
default:
return "0";
break;
}
case 'm':
switch (*namestr) {
case 'd':
case '1':
return "1";
break;
case 'g':
case '2':
return "2";
break;
case 's':
case '3':
return "3";
break;
case 'b':
case '4':
return "4";
break;
case 't':
switch (namestr[1]) {
case 'j':
return "4";
break;
case 'c':
return "5";
break;
default:
return "0";
}
case '5':
return "5";
break;
case '6':
return "6";
break;
case '7':
return "7";
break;
default:
return "0";
break;
}
case 'q':
switch (*namestr) {
case 'c':
case '1':
return "1";
break;
case 'b':
case '2':
return "2";
break;
case 'e':
case '3':
return "3";
break;
case 's':
case '4':
return "4";
break;
case 't':
return "5";
break;
default:
return "nn";
break;
}
case 'x':
if (isdigit_c(*namestr))
return namestr;
else
return "0";
break;
case 'u':
case 'w':
case 't':
case 'o':
case 'g':
case 'e':
case 's':
case 'y':
if (isdigit_c(*namestr))
return namestr;
else
return "0";
break;
case 'p':
if (isdigit_c(*namestr))
return namestr;
else
return "0";
break;
default:
return "0";
break;
}
}
Called from inp.c*/
void modprobenames(INPtables* tab) {
GENinstance* GENinst;
if (tab && tab->defVmod && tab->defVmod->GENinstances) {
for (GENinst = tab->defVmod->GENinstances; GENinst; GENinst = GENinst->GENnextInstance) {
char* name = GENinst->GENname;
if (prefix("vcurr_", name)) {
char* endname2;
char* endname = strchr(name, ':');
if (endname)
endname2 = strchr(endname + 1, ':');
else
continue;
if (!endname2) {
char* newname = copy_substring(name + 6, endname);
memcpy(name, newname, strlen(newname) + 1);
tfree(newname);
}
else {
char* newname = copy_substring(name + 6, endname2);
memcpy(name, newname, strlen(newname) + 1);
tfree(newname);
}
}
}
}
}
Polarity of VSRC is that its node 2 is directed towards the device, its node 1 towards the outer net connection.
If .probe p(Q1) is found, flag power is true, then do additional power calculations:
Define a reference voltage of an n-terminal device as Vref = (V(1) + V(2) +...+ V(n)) / n with terminal (node) voltages V(n).
Calculate power PQ1 = (v(1) - Vref) * i1 + (V(2) - Vref) * i2 + ... + (V(n) - Vref) * in) with terminal currents in.
See "Quantities of a Multiterminal Circuit Determined on the Basis of Kirchhoff�s Laws", M. Depenbrock,
ETEP Vol. 8, No. 4, July/August 1998.
probe_int_ is used to trigger supressing the vectors when saving the results. Internal vectors thus are
not saved. */
static int setallvsources(struct card *tmpcard, NGHASHPTR instances, char *instname, int numnodes, bool haveall, bool power)
{
struct card* card;
char* newline;
int nodenum;
int err = 0;
wordlist * allsaves = NULL;
if (haveall && !power)
return 2;
DS_CREATE(BVrefline, 200);
DS_CREATE(Bpowerline, 200);
DS_CREATE(Bpowersave, 200);
if (power) {
char numbuf[3];
sadd(&BVrefline, "Bprobe_int_");
sadd(&BVrefline, instname);
sadd(&BVrefline, "Vref ");
sadd(&BVrefline, instname);
sadd(&BVrefline, "probe_int_Vref 0 V = 1/");
sadd(&BVrefline, itoa10(numnodes, numbuf));
sadd(&BVrefline, "*(");
sadd(&Bpowerline, "Bprobe_int_");
sadd(&Bpowerline, instname);
sadd(&Bpowerline, "power ");
sadd(&Bpowerline, instname);
cadd(&Bpowerline, ':');
sadd(&Bpowerline, "power 0 V = 0+");
sadd(&Bpowersave, instname);
cadd(&Bpowersave, ':');
sadd(&Bpowersave, "power");
if (*instname == 'm' && strstr(tmpcard->line, "thermal"))
numnodes = 3;
if (*instname == 'm' && numnodes > 5)
numnodes = 5;
if (*instname == 'd')
numnodes = 2;
}
for (nodenum = 1; nodenum <= numnodes; nodenum++) {
int i;
char* instline = tmpcard->line;
for (i = nodenum; i > 0; i--) {
instline = nexttok(instline);
}
char* begstr = copy_substring(tmpcard->line, instline);
char* strnode1 = gettok(&instline);
Replace by _ (_VT).
Otherwise the B source function parser fails with i() not found. */
char* strnode1name = copy(strnode1);
if (strnode1name[0] == '/')
strnode1name[0] = '_';
char* newnode = tprintf("probe_int_%s_%s_%d", strnode1name, instname, nodenum);
char nodenumstr[3];
char *nodename1 = get_terminal_name(instname, itoa10(nodenum, nodenumstr), instances);
if (!nodename1) {
tfree(begstr);
tfree(strnode1);
tfree(strnode1name);
ds_free(&BVrefline);
ds_free(&Bpowerline);
ds_free(&Bpowersave);
return 3;
}
newline = tprintf("%s %s %s", begstr, newnode, instline);
char* vline = tprintf("vcurr_%s:probe_int_%s:%s_%s %s %s 0", instname, nodename1, nodenumstr, strnode1name, strnode1, newnode);
tfree(tmpcard->line);
tmpcard->line = newline;
card = tmpcard;
card = insert_new_line(card, vline, 0, card->linenum_orig, card->linesource);
if (power) {
if (nodenum == 1)
sadd(&BVrefline, "V(");
else
sadd(&BVrefline, "+V(");
sadd(&BVrefline, newnode);
cadd(&BVrefline, ')');
if (nodenum == 1)
sadd(&Bpowerline, "(V(");
else
sadd(&Bpowerline, "+(V(");
sadd(&Bpowerline, newnode);
sadd(&Bpowerline, ")-V(");
sadd(&Bpowerline, instname);
sadd(&Bpowerline, "probe_int_Vref))*i(vcurr_");
sadd(&Bpowerline, instname);
sadd(&Bpowerline, ":probe_int_");
sadd(&Bpowerline, nodename1);
cadd(&Bpowerline, ':');
sadd(&Bpowerline, nodenumstr);
cadd(&Bpowerline, '_');
sadd(&Bpowerline, strnode1name);
cadd(&Bpowerline, ')');
allsaves = wl_cons(copy(ds_get_buf(&Bpowersave)), allsaves);
}
tfree(begstr);
tfree(strnode1);
tfree(strnode1name);
tfree(newnode);
tfree(nodename1);
}
if (allsaves) {
allsaves = wl_cons(copy(".save"), allsaves);
char* newsaveline = wl_flatten(allsaves);
wl_free(allsaves);
allsaves = NULL;
card = tmpcard->nextcard;
card = insert_new_line(card, newsaveline, 0, card->linenum_orig, card->linesource);
}
if (power) {
cadd(&BVrefline, ')');
card = tmpcard->nextcard;
card = insert_new_line(card, copy(ds_get_buf(&BVrefline)), 0, card->linenum_orig, card->linesource);
card = insert_new_line(card, copy(ds_get_buf(&Bpowerline)), 0, card->linenum_orig, card->linesource);
}
ds_free(&BVrefline);
ds_free(&Bpowerline);
ds_free(&Bpowersave);
return err;
}
static int check_for_nodes(char* instance, int numnodes) {
int i;
char* tmpinst = instance;
tmpinst = nexttok(tmpinst);
for (i = 0; i < numnodes; i++) {
tmpinst = nexttok(tmpinst);
if (!tmpinst || *tmpinst == '\0') {
return 1;;
}
}
return 0;
}