xschem/src/rawtovcd.c

310 lines
8.4 KiB
C

/* File: rawtovcd.c
*
* This file is part of XSCHEM,
* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
* simulation.
* Copyright (C) 1998-2023 Stefan Frederik Schippers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* create a vcd file from a ngspice raw file containing a transient time simulation*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define BUFSIZE 4095
int binary_waves=0;
double voltage = 3;
double vth=2.5;
double vtl=0.5;
int debug = 1;
FILE *fd;
int nvars = 0 , npoints = 0;
double **values = NULL;
char **names = NULL, **vcd_ids = NULL;
double timescale=1e11; /* spice times will be multiplied by this number to get an integer */
double rel_timestep_precision = 5e-3;
double abs_timestep_precision = 1e-10;
void replace_bracket(char *s)
{
while(*s) {
/* if(*s =='[' || *s == ']') *s='_'; */
if(*s ==':') *s='.';
++s;
}
}
/* get a short unique ascii identifier to identify node */
const char *get_vcd_id(int idx)
{
static const char syms[] =
"0123456789abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ=+-_)(*&^%$#@!~`:;',\"<.>/?|";
static const int n = sizeof(syms)-1;
int q, r, pos;
static char res[32];
q = idx;
pos = 31;
res[pos] = '\0';
do {
pos--;
r = q % n;
q /= n;
res[pos] = syms[r];
} while(q && pos > 0);
return res + pos;
}
/* binary block is just a blob of npoints * nvars doubles */
void read_binary_block()
{
int p;
/* allocate storage for binary block */
values = calloc(npoints, sizeof(double *));
for(p = 0 ; p < npoints; p++) {
values[p] = calloc(nvars, sizeof(double));
}
/* read binary block */
for(p = 0; p < npoints; p++) {
if(fread(values[p], sizeof(double), nvars, fd) != nvars) {
fprintf(stderr, "Warning: binary block is not of correct size\n");
}
}
if(debug) fprintf(stderr, "done reading binary block\n");
}
/* parse ascii raw header section:
* returns: 1 if dataset and variables were read.
* 0 if transient sim dataset not found
* -1 on EOF
* Typical ascii header of raw file looks like:
*
* Title: **.subckt poweramp
* Date: Thu Nov 21 18:36:25 2019
* Plotname: Transient Analysis
* Flags: real
* No. Variables: 158
* No. Points: 90267
* Variables:
* 0 time time
* 1 v(net1) voltage
* 2 v(vss) voltage
* ...
* ...
* 155 i(v.x1.vd) current
* 156 i(v0) current
* 157 i(v1) current
* Binary:
*/
int read_dataset(void)
{
int variables = 0, i, done_points = 0;
char line[BUFSIZE + 1], varname[BUFSIZE + 1];
const char *id;
char *ptr;
int transient = 0;
npoints = 0;
nvars = 0;
while((ptr = fgets(line, sizeof(line), fd)) ) {
if(!strncmp(line, "Binary:", 7)) break; /* start of binary block */
if(transient) {
if(variables) {
sscanf(line, "%d %s", &i, varname); /* read index and name of saved waveform */
names[i] = malloc(strlen(varname) + 1);
strcpy(names[i], varname);
replace_bracket(names[i]);
id = get_vcd_id(i);
vcd_ids[i] = malloc(strlen(id) + 1) ;
strcpy(vcd_ids[i], id);
}
if(!strncmp(line, "Variables:", 10)) {
variables = 1;
names = calloc(nvars, sizeof(char *));
vcd_ids = calloc(nvars, sizeof(char *));
}
}
if(!strncmp(line, "No. of Data Rows :", 18)) {
sscanf(line, "No. of Data Rows : %d", &npoints);
done_points = 1;
}
if(!strncmp(line, "No. Variables:", 14)) {
sscanf(line, "No. Variables: %d", &nvars);
}
if(!strncmp(line, "Plotname: Transient Analysis", 28)) {
transient = 1;
}
if(!done_points && !strncmp(line, "No. Points:", 11)) {
sscanf(line, "No. Points: %d", &npoints);
}
}
if(!ptr) {
if(debug) fprintf(stderr, "EOF found\n");
variables = -1; /* EOF */
}
if(debug) fprintf(stderr, "npoints=%d, nvars=%d\n", npoints, nvars);
if(variables == 1) {
read_binary_block();
} else if(variables == 0) {
if(debug) fprintf(stderr, "seeking past binary block\n");
fseek(fd, nvars * npoints * sizeof(double), SEEK_CUR); /* skip binary block */
}
return variables;
}
unsigned char tobin(double v)
{
if(v > vth) return '1';
else if(v < vtl) return '0';
else return 'x';
}
void write_vcd_header()
{
char t[20];
int v;
printf("$timescale\n");
strcpy(t,
timescale == 1e12 ? "1ps" :
timescale == 1e11 ? "10ps" :
timescale == 1e10 ? "100ps" :
timescale == 1e9 ? "1ns" :
timescale == 1e8 ? "10ns" :
timescale == 1e7 ? "100ns" :
timescale == 1e6 ? "1us" :
timescale == 1e5 ? "10us" :
timescale == 1e4 ? "100us" :
timescale == 1e3 ? "1ms" :
timescale == 1e2 ? "10ms" :
timescale == 1e1 ? "100ms" :
timescale == 1 ? "1s" :
timescale == 1e-1 ? "10s" :
timescale == 1e-2 ? "100s" :
"1000s");
printf(" %s\n", t);
printf("$end\n");
for(v = 1; v < nvars; v++) {
if(binary_waves) printf("$var reg 1 %s %s $end\n", vcd_ids[v], names[v]);
else printf("$var real 1 %s %s $end\n", vcd_ids[v], names[v]);
}
printf("$enddefinitions $end\n");
}
void dump_vcd_waves()
{
int p, v;
double *lastvalue, val;
lastvalue = malloc(nvars * sizeof(double));
for(p = 0; p < npoints; p++) {
if(p == 0) {
printf("#0\n");
printf("$dumpvars\n");
for(v = 1 ; val = values[p][v], v < nvars; v++) {
if(binary_waves) printf("%c%s\n", tobin(val), vcd_ids[v]);
else printf("r%.3g %s\n", val, vcd_ids[v]);
if(binary_waves) lastvalue[v] = tobin(val);
else lastvalue[v] = val;
}
printf("$end\n");
} else {
printf("#%d\n", (int) (values[p][0] * timescale));
for(v = 1 ; val = values[p][v], v < nvars; v++) {
if(binary_waves) {
if( tobin(val) != lastvalue[v] ) {
printf("%c%s\n", tobin(val), vcd_ids[v]);
lastvalue[v] = tobin(val);
}
} else {
if(
(val != 0.0 && fabs((val - lastvalue[v]) / val) > rel_timestep_precision) ||
(val == 0.0 && fabs(val - lastvalue[v]) > abs_timestep_precision)
) {
printf("r%.3g %s\n", val, vcd_ids[v]);
lastvalue[v] = val;
}
}
}
}
}
free(lastvalue);
}
void free_storage()
{
int i;
for(i = 0 ; i < nvars; ++i) {
free(names[i]);
free(vcd_ids[i]);
}
for(i = 0 ; i < npoints; ++i) {
free(values[i]);
}
free(values);
free(names);
free(vcd_ids);
}
int main(int argc, char *argv[])
{
int res;
int i = 1;
for(i = 1; i < argc; ++i) {
if(!strcmp(argv[i], "-v")) {
++i;
if(i + 1 >= argc) continue;
binary_waves = 1;
voltage = atof(argv[i]);
vth = voltage * 0.75;
vtl = voltage * 0.25;
} else if(argv[i][0] == '-') {
++i;
} else {
break;
}
}
if(i >= argc) {
fprintf(stderr, "Rawtovcd: convert a spice RAW file to VCD\n");
fprintf(stderr, "If '-v voltage' is given transform waves to digital (binary values)\n");
fprintf(stderr, "usage: rawtovcd [-v voltage] rawfile > vcdfile\n");
exit(EXIT_FAILURE);
}
if(!strcmp(argv[i], "-")) fd = stdin;
else fd = fopen(argv[i], "r");
if(fd) for(;;) {
if((res = read_dataset()) == 1) {
write_vcd_header();
dump_vcd_waves();
free_storage();
break;
} else if(res == -1) { /* EOF */
fprintf(stderr, "rawtovcd: dataset not found in raw file\n");
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "rawtovcd: failed to open file for reading\n");
return EXIT_FAILURE;
}
if(fd && fd != stdin) fclose(fd);
return EXIT_SUCCESS;
}