diff --git a/src/include/ngspice/lxt2_write.h b/src/include/ngspice/lxt2_write.h new file mode 100644 index 000000000..a15b1b809 --- /dev/null +++ b/src/include/ngspice/lxt2_write.h @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2003-2010 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DEFS_LXTW_H +#define DEFS_LXTW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WAVE_ALLOCA_H +#define WAVE_ALLOCA_H +#include +#include +#define wave_alloca alloca +#define LXT2_WR_HDRID (0x1380) +#define LXT2_WR_VERSION (0x0001) + +#define LXT2_WR_GRANULE_SIZE (64) +#define LXT2_WR_GRANULE_NUM (256) +#define LXT2_WR_PARTIAL_SIZE (2048) + +#define LXT2_WR_GRAN_SECT_TIME 0 +#define LXT2_WR_GRAN_SECT_DICT 1 +#define LXT2_WR_GRAN_SECT_TIME_PARTIAL 2 + +#define LXT2_WR_GZWRITE_BUFFER 4096 +#define LXT2_WR_SYMPRIME 500009 + +typedef uint64_t lxttime_t; + + +#define LXT2_WR_LLD "%lld" +#define LXT2_WR_LLDESC(x) x##LL +#define LXT2_WR_ULLDESC(x) x##ULL + +#if LXT2_WR_GRANULE_SIZE > 32 +typedef unsigned long long granmsk_t; +#define LXT2_WR_GRAN_0VAL (LXT2_WR_ULLDESC(0)) +#define LXT2_WR_GRAN_1VAL (LXT2_WR_ULLDESC(1)) +#else +typedef unsigned int granmsk_t; +#define LXT2_WR_GRAN_0VAL (0) +#define LXT2_WR_GRAN_1VAL (1) +#endif + + +enum LXT2_WR_Encodings { + LXT2_WR_ENC_0, + LXT2_WR_ENC_1, + LXT2_WR_ENC_INV, + LXT2_WR_ENC_LSH0, + LXT2_WR_ENC_LSH1, + LXT2_WR_ENC_RSH0, + LXT2_WR_ENC_RSH1, + + LXT2_WR_ENC_ADD1, + LXT2_WR_ENC_ADD2, + LXT2_WR_ENC_ADD3, + LXT2_WR_ENC_ADD4, + + LXT2_WR_ENC_SUB1, + LXT2_WR_ENC_SUB2, + LXT2_WR_ENC_SUB3, + LXT2_WR_ENC_SUB4, + + LXT2_WR_ENC_X, + LXT2_WR_ENC_Z, + + LXT2_WR_ENC_BLACKOUT, + + LXT2_WR_DICT_START + }; + +/* + * integer splay + */ +typedef struct lxt2_wr_ds_tree_node lxt2_wr_ds_Tree; +struct lxt2_wr_ds_tree_node { + lxt2_wr_ds_Tree * left, * right; + granmsk_t item; + int val; + lxt2_wr_ds_Tree * next; +}; + + +/* + * string splay + */ +typedef struct lxt2_wr_dslxt_tree_node lxt2_wr_dslxt_Tree; +struct lxt2_wr_dslxt_tree_node { + lxt2_wr_dslxt_Tree * left, * right; + char *item; + unsigned int val; + lxt2_wr_dslxt_Tree * next; +}; + + +struct lxt2_wr_trace +{ +FILE *handle; +gzFile zhandle; + +lxt2_wr_dslxt_Tree *dict; /* dictionary manipulation */ +unsigned int num_dict_entries; +unsigned int dict_string_mem_required; +lxt2_wr_dslxt_Tree *dict_head; +lxt2_wr_dslxt_Tree *dict_curr; + +lxt2_wr_ds_Tree *mapdict; /* bitmap compression */ +unsigned int num_map_entries; +lxt2_wr_ds_Tree *mapdict_head; +lxt2_wr_ds_Tree *mapdict_curr; + +off_t position; +off_t zfacname_predec_size, zfacname_size, zfacgeometry_size; +off_t zpackcount, zpackcount_cumulative; +off_t current_chunk, current_chunkz; + +struct lxt2_wr_symbol *sym[LXT2_WR_SYMPRIME]; +struct lxt2_wr_symbol **sorted_facs; +struct lxt2_wr_symbol *symchain; +int numfacs, numalias; +int numfacbytes; +int longestname; + +int numsections, numblock; +off_t facname_offset, facgeometry_offset; + +lxttime_t mintime, maxtime; +unsigned int timegranule; +int timescale; +int timepos; +unsigned int maxgranule; +lxttime_t firsttime, lasttime; +lxttime_t timetable[LXT2_WR_GRANULE_SIZE]; + +unsigned int partial_iter; + +char *compress_fac_str; +int compress_fac_len; + +lxttime_t flushtime; +unsigned flush_valid : 1; + +unsigned do_strip_brackets : 1; +unsigned emitted : 1; /* gate off change field zmode changes when set */ +unsigned timeset : 1; /* time has been modified from 0..0 */ +unsigned bumptime : 1; /* says that must go to next time position in granule as value change exists for current time */ +unsigned granule_dirty : 1; /* for flushing out final block */ +unsigned blackout : 1; /* blackout on/off */ +unsigned partial : 1; /* partial (vertical) trace support */ +unsigned partial_zip : 1; /* partial (vertical) trace support for zip subregions */ +unsigned no_checkpoint : 1; /* turns off interblock checkpointing */ +unsigned partial_preference : 1; /* partial preference encountered on some facs */ + +char initial_value; + +char zmode[4]; /* fills in with "wb0".."wb9" */ +unsigned int gzbufpnt; +unsigned char gzdest[LXT2_WR_GZWRITE_BUFFER + 4]; /* enough for zlib buffering */ + +char *lxtname; +off_t break_size; +off_t break_header_size; +unsigned int break_number; +}; + + +struct lxt2_wr_symbol +{ +struct lxt2_wr_symbol *next; +struct lxt2_wr_symbol *symchain; +char *name; +int namlen; + +int facnum; +struct lxt2_wr_symbol *aliased_to; + +char *value; /* fac's actual value */ + +unsigned int rows; +int msb, lsb; +int len; +int flags; + +unsigned partial_preference : 1; /* in order to shove nets to the first partial group */ + +unsigned int chgpos; +granmsk_t msk; /* must contain LXT2_WR_GRANULE_SIZE bits! */ +unsigned int chg[LXT2_WR_GRANULE_SIZE]; +}; + + +#define LXT2_WR_SYM_F_BITS (0) +#define LXT2_WR_SYM_F_INTEGER (1<<0) +#define LXT2_WR_SYM_F_DOUBLE (1<<1) +#define LXT2_WR_SYM_F_STRING (1<<2) +#define LXT2_WR_SYM_F_TIME (LXT2_WR_SYM_F_STRING) /* user must correctly format this as a string */ +#define LXT2_WR_SYM_F_ALIAS (1<<3) + +#define LXT2_WR_SYM_F_SIGNED (1<<4) +#define LXT2_WR_SYM_F_BOOLEAN (1<<5) +#define LXT2_WR_SYM_F_NATURAL ((1<<6)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_POSITIVE ((1<<7)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_CHARACTER (1<<8) + +#define LXT2_WR_SYM_F_CONSTANT (1<<9) +#define LXT2_WR_SYM_F_VARIABLE (1<<10) +#define LXT2_WR_SYM_F_SIGNAL (1<<11) + +#define LXT2_WR_SYM_F_IN (1<<12) +#define LXT2_WR_SYM_F_OUT (1<<13) +#define LXT2_WR_SYM_F_INOUT (1<<14) + +#define LXT2_WR_SYM_F_WIRE (1<<15) +#define LXT2_WR_SYM_F_REG (1<<16) + +void lxt2_init(void *plotPtr); +void lxt2_end(void *plotPtr); + + /* file I/O */ +struct lxt2_wr_trace * lxt2_wr_init(const char *name); +void lxt2_wr_flush(struct lxt2_wr_trace *lt); +void lxt2_wr_close(struct lxt2_wr_trace *lt); + + /* for dealing with very large traces, split into multiple files approximately "siz" in length */ +void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz); + + /* 0 = no compression, 9 = best compression, 4 = default */ +void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth); + + /* default is partial off, turning on makes for faster trace reads, nonzero zipmode causes vertical compression */ +void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode); +void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name); + + /* turning off checkpointing makes for smaller files */ +void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt); + + /* facility creation */ +void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value); +struct lxt2_wr_symbol * lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name); +struct lxt2_wr_symbol * lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); +struct lxt2_wr_symbol * lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); +void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit); + + /* each granule is LXT2_WR_GRANULE_SIZE (32 or 64) timesteps, default is 256 per section */ +void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule); + + /* time ops */ +void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale); +int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval); +int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval); + + /* allows blackout regions in LXT files */ +void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt); +void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt); + + /* left fill on bit_string uses vcd semantics (left fill with value[0] unless value[0]=='1', then use '0') */ +int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value); +int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value); +int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); +int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); + +typedef struct lxt2_s { + struct lxt2_wr_trace *file; + struct lxt2_wr_symbol **evt_table; + int *evt_indexmap; + int evt_num; + struct lxt2_wr_symbol **kvl_table; + int *kvl_indexmap; + int kvl_num; +} lxt2_t; + +#endif + + +#endif diff --git a/src/spicelib/analysis/cktlxt2.c b/src/spicelib/analysis/cktlxt2.c new file mode 100644 index 000000000..73196fdaf --- /dev/null +++ b/src/spicelib/analysis/cktlxt2.c @@ -0,0 +1,90 @@ +#include "ngspice/ngspice.h" +#include "ngspice/config.h" +#include "ngspice/cktdefs.h" +//#include "ngspice/cktaccept.h" +#include "ngspice/trandefs.h" +#include "ngspice/sperror.h" +#include "ngspice/fteext.h" +#include "ngspice/missing_math.h" +#include "../frontend/outitf.h" +#include + +#ifdef XSPICE +/* gtri - add - wbk - Add headers */ +#include "ngspice/miftypes.h" + +#include "ngspice/evt.h" +#include "ngspice/mif.h" +#include "ngspice/evtproto.h" +#include "ngspice/ipctiein.h" +/* gtri - end - wbk - Add headers */ +#endif + +#include "ngspice/lxt2_write.h" + +#ifdef CLUSTER +#include "cluster.h" +#endif + +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (type *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + +void CKTemitlxt2(void *plotPtr) +{ + runDesc *run = (runDesc *) plotPtr; + struct lxt2_wr_symbol **trace_table; + struct lxt2_wr_symbol *trace; + char *name; + double value; + static unsigned int last_set_time, set_time; + static double time_resolution=1.0e-9; + int i,trace_num; + int *trace_index; + CKTcircuit *ckt; + IFvalue valData; + + ckt=run->circuit; + trace_table = run->circuit->lxt2.kvl_table; + trace_num = ckt->lxt2.kvl_num; + trace_index = ckt->lxt2.kvl_indexmap; + + valData.v.numValue = ckt->CKTmaxEqNum-1; + valData.v.vec.rVec = ckt->CKTrhsOld+1; + + if(ckt->CKTtime <= 0.0) { + time_resolution=(double)pow(10.0,(double)LXT2_TIME_RESOLUTION_EXPONENT); + last_set_time = 0; + #ifdef LXT2_DEBUG + printf("LXT2 CKTemitlxt2 set time (%g) (%d)\n", ckt->CKTtime,last_set_time); + #endif + lxt2_wr_set_time(ckt->lxt2.file, last_set_time); + } + set_time = (unsigned int)(ckt->CKTtime/time_resolution); + if(set_time > last_set_time) { + #ifdef LXT2_DEBUG + printf("LXT2 CKTemitlxt2 set time (%g) (%d)\n", ckt->CKTtime,set_time); + #endif + lxt2_wr_set_time(ckt->lxt2.file, set_time); + last_set_time = set_time; + } + for(i = 0; i < trace_num; i++) { + if(trace_index[i] > 0) { + trace=(struct lxt2_wr_symbol *)(trace_table[i]); + if(trace!=NULL) { + value = valData.v.vec.rVec[run->data[trace_index[i]].outIndex]; + name = run->data[trace_index[i]].name; + #ifdef LXT2_DEBUG + printf("LXT2 cktlxt2.c:trace(%p) emit double (%s)->(%g)\n",&trace,name,value); + #endif + lxt2_wr_emit_value_double(ckt->lxt2.file,trace,0,value); + } + } + } + #ifdef LXT2_DEBUG + printf("LXT2 cktlxt2.c: flush ckt.tracelxt2\n"); + #endif + lxt2_wr_flush(ckt->lxt2.file); +} diff --git a/src/spicelib/analysis/lxt2_init.c b/src/spicelib/analysis/lxt2_init.c new file mode 100644 index 000000000..26f568c70 --- /dev/null +++ b/src/spicelib/analysis/lxt2_init.c @@ -0,0 +1,162 @@ +#include "ngspice/ngspice.h" +#include "ngspice/config.h" +#include "ngspice/cktdefs.h" +//#include "ngspice/cktaccept.h" +#include "ngspice/trandefs.h" +#include "ngspice/sperror.h" +#include "ngspice/fteext.h" +#include "ngspice/missing_math.h" +#include "../frontend/outitf.h" + +#ifdef XSPICE +/* gtri - add - wbk - Add headers */ +#include "ngspice/miftypes.h" + +#include "ngspice/evt.h" +#include "ngspice/mif.h" +#include "ngspice/evtproto.h" +#include "ngspice/ipctiein.h" +/* gtri - end - wbk - Add headers */ +#endif + +#include "ngspice/lxt2_write.h" + +#ifdef CLUSTER +#include "cluster.h" +#endif + +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (type *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + +static void EVTtraceinit(CKTcircuit *ckt); +static void KVLtraceinit(void *plotPtr); + +void lxt2_init(void *plotPtr) { + runDesc *run = (runDesc *)plotPtr; + + #ifdef LXT2_DEBUG + printf("LXT2 lxt2_init\n"); + #endif + + if(run && run->circuit) { + #ifdef LXT2_DEBUG + printf("LXT2 run and run->circuit exist\n"); + printf("\nLXT2 run->name(%s)\n",run->name); + printf("\nLXT2 Spice_Path(%s)\n",Spice_Path); + #endif + run->circuit->lxt2.file = lxt2_wr_init("waveforms.lxt"); + lxt2_wr_set_timescale(run->circuit->lxt2.file, LXT2_TIME_RESOLUTION_EXPONENT); + printf("LXT2 time resolution is 1.0e%d seconds.\n\n",LXT2_TIME_RESOLUTION_EXPONENT); + fflush(stdout); + EVTtraceinit(run->circuit); + KVLtraceinit(run); + } +} + +void lxt2_end(void *plotPtr) +{ + runDesc *run = (runDesc *) plotPtr; + if(run && run->circuit) { + #ifdef LXT2_DEBUG + printf("LXT2 final file flush.\n"); + #endif + lxt2_wr_flush(run->circuit->lxt2.file); + #ifdef LXT2_DEBUG + printf("LXT2 close file.\n"); + #endif + lxt2_wr_close(run->circuit->lxt2.file); + free(run->circuit->lxt2.evt_table); + free(run->circuit->lxt2.evt_indexmap); + free(run->circuit->lxt2.kvl_table); + free(run->circuit->lxt2.kvl_indexmap); + } +} + +static void EVTtraceinit(CKTcircuit *ckt) /* the circuit structure */ +{ + + struct lxt2_wr_symbol **trace_table = NULL; /* holmes: vector of pointer to traces in lxt2 output file */ + Evt_Node_Info_t **node_table; + int i; + int num_nodes; + + node_table = ckt->evt->info.node_table;; + num_nodes = ckt->evt->counts.num_nodes; + + int *evt_indexmap = NULL; + + /* holmes: Allocate and initialize table of lxt2 evt trace pointers */ + #ifdef LXT2_DEBUG + printf("LXT2 Allocate trace_table\n"); + #endif + CKALLOC(trace_table, num_nodes, struct lxt2_wr_symbol *); + CKALLOC(evt_indexmap, num_nodes, int); + for(i = 0; i < num_nodes; i++) { + evt_indexmap[i] = i; + switch(node_table[i]->udn_index) + { + case 0: /* Bit */ + trace_table[i] = lxt2_wr_symbol_add(ckt->lxt2.file, node_table[i]->name, 0, 0, 0, LXT2_WR_SYM_F_BITS); + #ifdef LXT2_DEBUG + printf("LXT2 created EVT BIT trace_table[%d] pointer(%p) name(%s)\n",i,&trace_table[i],node_table[i]->name); + #endif + break; + case 1: /* Integer */ + trace_table[i] = lxt2_wr_symbol_add(ckt->lxt2.file, node_table[i]->name, 0, 0, 0, LXT2_WR_SYM_F_INTEGER); + #ifdef LXT2_DEBUG + printf("LXT2 created EVT INT trace_table[%d] pointer(%p) name(%s)\n",i,&trace_table[i],node_table[i]->name); + #endif + break; + case 2: /* Double */ + trace_table[i] = lxt2_wr_symbol_add(ckt->lxt2.file, node_table[i]->name, 0, 0, 0, LXT2_WR_SYM_F_DOUBLE); + #ifdef LXT2_DEBUG + printf("LXT2 created EVT DUB trace_table[%d] pointer(%p) name(%s)\n",i,&trace_table[i],node_table[i]->name); + #endif + break; + default: /* Bit */ + trace_table[i] = lxt2_wr_symbol_add(ckt->lxt2.file, node_table[i]->name, 0, 0, 0, LXT2_WR_SYM_F_BITS); + #ifdef LXT2_DEBUG + printf("LXT2 created EVT BIT trace_table[%d] pointer(%p) name(%s)\n",i,&trace_table[i],node_table[i]->name); + #endif + break; + } + } + fflush(stdout); + ckt->lxt2.evt_indexmap = evt_indexmap; + ckt->lxt2.evt_table = trace_table; + ckt->lxt2.evt_num = num_nodes; +} + +static void KVLtraceinit(void *plotPtr) /* Call this after OUTpBeginPlot after all electrical run data is loaded */ +{ + runDesc *run = (runDesc *) plotPtr; + struct lxt2_wr_symbol **trace_table = NULL; + int *kvl_indexmap = NULL; + int i; + if(run->circuit) { + CKALLOC(trace_table, run->numData, struct lxt2_wr_symbol *); + CKALLOC(kvl_indexmap, run->numData, int); + for(i = 0; i < run->numData; i++) { + kvl_indexmap[i] = -1; + trace_table[i] = NULL; + if (run->data[i].outIndex != -1) { /* Skip over the time vector. Once we parse the saves list this check won't be necessary. */ + if (run->data[i].regular) { /* Check that the data is not "special" like a parameter, might allow this later */ + if (run->data[i].type == IF_REAL) { /* As opposed to being complex */ + kvl_indexmap[i] = i; + trace_table[i] = lxt2_wr_symbol_add(run->circuit->lxt2.file, run->data[i].name, 0, 0, 0, LXT2_WR_SYM_F_DOUBLE); + #ifdef LXT2_DEBUG + printf("LXT2 created KVL DUB trace_table[%d] pointer(%p) name(%s)\n",i,&trace_table[i],run->data[i].name); + #endif + fflush(stdout); + } + } + } + } + run->circuit->lxt2.kvl_indexmap = kvl_indexmap; + run->circuit->lxt2.kvl_table = trace_table; + run->circuit->lxt2.kvl_num = run->numData; + } +} diff --git a/src/spicelib/analysis/lxt2_write.c b/src/spicelib/analysis/lxt2_write.c new file mode 100644 index 000000000..aa8ebb27f --- /dev/null +++ b/src/spicelib/analysis/lxt2_write.c @@ -0,0 +1,2198 @@ +/* + * Copyright (c) 2003-2008 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef _AIX +#pragma alloca +#endif + +#include "ngspice/config.h" +#include "ngspice/lxt2_write.h" + + +static char *lxt2_wr_vcd_truncate_bitvec(char *s) +{ +char l, r; + +r=*s; +if(r=='1') + { + return s; + } + else + { + s++; + } + +for(;;s++) + { + l=r; r=*s; + if(!r) return (s-1); + + if(l!=r) + { + return(((l=='0')&&(r=='1'))?s:s-1); + } + } +} + + +/* + * in-place sort to keep chained facs from migrating... + */ +static void wave_mergesort(struct lxt2_wr_symbol **a, struct lxt2_wr_symbol **b, int lo, int hi) +{ +int i, j, k; + +if (loname, a[j]->name) <= 0) + { + a[k++]=b[i++]; + } + else + { + a[k++]=a[j++]; + } + } + + while (kitem) { + if (t->left == NULL) break; + if (i < t->left->item) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (i > t->item) { + if (t->right == NULL) break; + if (i > t->right->item) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + + +static lxt2_wr_ds_Tree * lxt2_wr_ds_insert(granmsk_t i, lxt2_wr_ds_Tree * t, int val) { +/* Insert i into the tree t, unless it's already there. */ +/* Return a pointer to the resulting tree. */ + lxt2_wr_ds_Tree * n; + + n = (lxt2_wr_ds_Tree *) calloc (1, sizeof (lxt2_wr_ds_Tree)); + if (n == NULL) { + fprintf(stderr, "ds_insert: ran out of memory, exiting.\n"); + exit(255); + } + n->item = i; + n->val = val; + if (t == NULL) { + n->left = n->right = NULL; + return n; + } + t = lxt2_wr_ds_splay(i,t); + if (i < t->item) { + n->left = t->left; + n->right = t; + t->left = NULL; + return n; + } else if (i > t->item) { + n->right = t->right; + n->left = t; + t->right = NULL; + return n; + } else { /* We get here if it's already in the tree */ + /* Don't add it again */ + free(n); + return t; + } +} + +/************************ splay ************************/ + +static int lxt2_wr_dslxt_success; + +static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_splay (char *i, lxt2_wr_dslxt_Tree * t) { +/* Simple top down splay, not requiring i to be in the tree t. */ +/* What it does is described above. */ + lxt2_wr_dslxt_Tree N, *l, *r, *y; + int dir; + + lxt2_wr_dslxt_success = 0; + + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + + for (;;) { + dir = strcmp(i, t->item); + if (dir < 0) { + if (t->left == NULL) break; + if (strcmp(i, t->left->item)<0) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (dir > 0) { + if (t->right == NULL) break; + if (strcmp(i, t->right->item)>0) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + lxt2_wr_dslxt_success=1; + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + return t; +} + + +static lxt2_wr_dslxt_Tree * lxt2_wr_dslxt_insert(char *i, lxt2_wr_dslxt_Tree * t, unsigned int val) { +/* Insert i into the tree t, unless it's already there. */ +/* Return a pointer to the resulting tree. */ + lxt2_wr_dslxt_Tree * n; + int dir; + + n = (lxt2_wr_dslxt_Tree *) calloc (1, sizeof (lxt2_wr_dslxt_Tree)); + if (n == NULL) { + fprintf(stderr, "dslxt_insert: ran out of memory, exiting.\n"); + exit(255); + } + n->item = i; + n->val = val; + if (t == NULL) { + n->left = n->right = NULL; + return n; + } + t = lxt2_wr_dslxt_splay(i,t); + dir = strcmp(i,t->item); + if (dir<0) { + n->left = t->left; + n->right = t; + t->left = NULL; + return n; + } else if (dir>0) { + n->right = t->right; + n->left = t; + t->right = NULL; + return n; + } else { /* We get here if it's already in the tree */ + /* Don't add it again */ + free(n); + return t; + } +} + +/************************ splay ************************/ + +/* + * functions which emit various big endian + * data to a file + */ +static int lxt2_wr_emit_u8(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[1]; +int nmemb; + +buf[0] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 1, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u16(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[2]; +int nmemb; + +buf[0] = (value>>8) & 0xff; +buf[1] = value & 0xff; +nmemb = fwrite(buf, sizeof(char), 2, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u32(struct lxt2_wr_trace *lt, int value) +{ +unsigned char buf[4]; +int nmemb; + +buf[0] = (value>>24) & 0xff; +buf[1] = (value>>16) & 0xff; +buf[2] = (value>>8) & 0xff; +buf[3] = value & 0xff; +nmemb=fwrite(buf, sizeof(char), 4, lt->handle); +lt->position+=nmemb; +return(nmemb); +} + + +static int lxt2_wr_emit_u64(struct lxt2_wr_trace *lt, int valueh, int valuel) +{ +int rc; + +if((rc=lxt2_wr_emit_u32(lt, valueh))) + { + rc=lxt2_wr_emit_u32(lt, valuel); + } + +return(rc); +} + + +/* + * gzfunctions which emit various big endian + * data to a file. (lt->position needs to be + * fixed up on gzclose so the tables don't + * get out of sync!) + */ +static int gzwrite_buffered(struct lxt2_wr_trace *lt) +{ +int rc = 1; + +if(lt->gzbufpnt > LXT2_WR_GZWRITE_BUFFER) + { + rc = gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); + rc = rc ? 1 : 0; + lt->gzbufpnt = 0; + } + +return(rc); +} + +static void gzflush_buffered(struct lxt2_wr_trace *lt, int doclose) +{ +if(lt->gzbufpnt) + { + gzwrite(lt->zhandle, lt->gzdest, lt->gzbufpnt); + lt->gzbufpnt = 0; + if(!doclose) + { + gzflush(lt->zhandle, Z_SYNC_FLUSH); + } + } + +if(doclose) + { + gzclose(lt->zhandle); + } +} + + +static int lxt2_wr_emit_u8z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = value & 0xff; + +nmemb=gzwrite_buffered(lt); +lt->zpackcount++; +lt->position++; +return(nmemb); +} + + +static int lxt2_wr_emit_u16z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb = gzwrite_buffered(lt); +lt->zpackcount+=2; +lt->position+=2; +return(nmemb); +} + + +static int lxt2_wr_emit_u24z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb=gzwrite_buffered(lt); +lt->zpackcount+=3; +lt->position+=3; +return(nmemb); +} + + +static int lxt2_wr_emit_u32z(struct lxt2_wr_trace *lt, int value) +{ +int nmemb; + +lt->gzdest[lt->gzbufpnt++] = (value>>24) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>16) & 0xff; +lt->gzdest[lt->gzbufpnt++] = (value>>8) & 0xff; +lt->gzdest[lt->gzbufpnt++] = value & 0xff; +nmemb=gzwrite_buffered(lt); + +lt->zpackcount+=4; +lt->position+=4; +return(nmemb); +} + + +static int lxt2_wr_emit_u64z(struct lxt2_wr_trace *lt, int valueh, int valuel) +{ +int rc; + +if((rc=lxt2_wr_emit_u32z(lt, valueh))) + { + rc=lxt2_wr_emit_u32z(lt, valuel); + } + +return(rc); +} + + +static int lxt2_wr_emit_stringz(struct lxt2_wr_trace *lt, char *value) +{ +int rc=1; +do + { + rc&=lxt2_wr_emit_u8z(lt, *value); + } while(*(value++)); +return(rc); +} + + +/* + * hash/symtable manipulation + */ +static int lxt2_wr_hash(const char *s) +{ +const char *p; +char ch; +unsigned int h=0, h2=0, pos=0, g; +for(p=s;*p;p++) + { + ch=*p; + h2<<=3; + h2-=((unsigned int)ch+(pos++)); /* this handles stranded vectors quite well.. */ + + h=(h<<4)+ch; + if((g=h&0xf0000000)) + { + h=h^(g>>24); + h=h^g; + } + } + +h^=h2; /* combine the two hashes */ +return(h%LXT2_WR_SYMPRIME); +} + + +static struct lxt2_wr_symbol *lxt2_wr_symadd(struct lxt2_wr_trace *lt, const char *name, int hv) +{ +struct lxt2_wr_symbol *s; + +s=(struct lxt2_wr_symbol *)calloc(1,sizeof(struct lxt2_wr_symbol)); +strcpy(s->name=(char *)malloc((s->namlen=strlen(name))+1),name); +s->next=lt->sym[hv]; +lt->sym[hv]=s; +return(s); +} + + +static struct lxt2_wr_symbol *lxt2_wr_symfind(struct lxt2_wr_trace *lt, const char *s) +{ +int hv; +struct lxt2_wr_symbol *temp; + +hv=lxt2_wr_hash(s); +if(!(temp=lt->sym[hv])) return(NULL); /* no hash entry, add here wanted to add */ + +while(temp) + { + if(!strcmp(temp->name,s)) + { + return(temp); /* in table already */ + } + if(!temp->next) break; + temp=temp->next; + } + +return(NULL); /* not found, add here if you want to add*/ +} + + +/* + * compress facs to a prefix count + string + 0x00 + */ +static void lxt2_wr_compress_fac(struct lxt2_wr_trace *lt, char *str) +{ +int i; +int len = strlen(str); +int minlen = (lencompress_fac_len) ? len : lt->compress_fac_len; + +if(minlen>65535) minlen=65535; /* keep in printable range--most hierarchies won't be this big anyway */ + +if(lt->compress_fac_str) + { + for(i=0;icompress_fac_str[i]!=str[i]) break; + } + lxt2_wr_emit_u16z(lt, i); + lxt2_wr_emit_stringz(lt, str+i); + free(lt->compress_fac_str); + } + else + { + lxt2_wr_emit_u16z(lt, 0); + lxt2_wr_emit_stringz(lt, str); + } + +lt->compress_fac_str = (char *) malloc((lt->compress_fac_len=len)+1); +strcpy(lt->compress_fac_str, str); +} + + +/* + * emit facs in sorted order along with geometry + * and sync table info + */ + +static void strip_brack(struct lxt2_wr_symbol *s) +{ +char *lastch = s->name+s->namlen - 1; +if(*lastch!=']') return; +if(s->namlen<3) return; +lastch--; +while(lastch!=s->name) + { + if(*lastch=='.') + { + return; /* MTI SV [0.3] notation for implicit vars */ + } + + if(*lastch=='[') + { + *lastch=0x00; + return; + } + lastch--; + } +return; +} + + +static void lxt2_wr_emitfacs(struct lxt2_wr_trace *lt) +{ +int i; + +if((lt)&&(lt->numfacs)) + { + struct lxt2_wr_symbol *s = lt->symchain; + struct lxt2_wr_symbol **aliascache = calloc(lt->numalias ? lt->numalias : 1, sizeof(struct lxt2_wr_symbol *)); + int aliases_encountered, facs_encountered; + + lt->sorted_facs = (struct lxt2_wr_symbol **)calloc(lt->numfacs, sizeof(struct lxt2_wr_symbol *)); + + if(lt->sorted_facs && aliascache) + { + if(lt->do_strip_brackets) + for(i=0;inumfacs;i++) + { + lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ + strip_brack(s); + s=s->symchain; + } + else + for(i=0;inumfacs;i++) + { + lt->sorted_facs[lt->numfacs - i - 1] = s; /* facs were chained backwards so reverse to restore bitslicing */ + s=s->symchain; + } + wave_msort(lt->sorted_facs, lt->numfacs); + + if(lt->partial_preference) + { + /* move preferenced facs up */ + struct lxt2_wr_symbol **prefcache = aliascache; + int prefs_encountered = 0; + + facs_encountered = 0; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->partial_preference)==0) + { + lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; + facs_encountered++; + } + else + { + prefcache[prefs_encountered] = lt->sorted_facs[i]; + prefs_encountered++; + } + } + /* then append the non-preferenced facs */ + for(i=0;isorted_facs[i]; + } + + /* now make prefcache the main cache */ + aliascache = lt->sorted_facs; + lt->sorted_facs = prefcache; + } + + /* move facs up */ + aliases_encountered = 0, facs_encountered = 0; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) + { + lt->sorted_facs[facs_encountered] = lt->sorted_facs[i]; + facs_encountered++; + } + else + { + aliascache[aliases_encountered] = lt->sorted_facs[i]; + aliases_encountered++; + } + } + /* then append the aliases */ + for(i=0;isorted_facs[facs_encountered+i] = aliascache[i]; + } + + + for(i=0;inumfacs;i++) + { + lt->sorted_facs[i]->facnum = i; + } + + lt->facname_offset=lt->position; + + lxt2_wr_emit_u32(lt, lt->numfacs); /* uncompressed */ + lxt2_wr_emit_u32(lt, lt->numfacbytes); /* uncompressed */ + lxt2_wr_emit_u32(lt, lt->longestname); /* uncompressed */ + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacnamesize */ + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacname_predec_size */ + lxt2_wr_emit_u32(lt, 0); /* uncompressed : placeholder for zfacgeometrysize */ + lxt2_wr_emit_u8(lt, lt->timescale); /* timescale (-9 default == nsec) */ + + fflush(lt->handle); + lt->zfacname_size = lt->position; + lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); + + lt->zpackcount = 0; + for(i=0;inumfacs;i++) + { + lxt2_wr_compress_fac(lt, lt->sorted_facs[i]->name); + free(lt->sorted_facs[i]->name); + lt->sorted_facs[i]->name = NULL; + } + free(lt->compress_fac_str); lt->compress_fac_str=NULL; + lt->compress_fac_len=0; + lt->zfacname_predec_size = lt->zpackcount; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + lt->zfacname_size = lt->position - lt->zfacname_size; + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), "wb9"); + + lt->facgeometry_offset = lt->position; + for(i=0;inumfacs;i++) + { + if((lt->sorted_facs[i]->flags&LXT2_WR_SYM_F_ALIAS)==0) + { + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->rows); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->flags); + } + else + { + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->aliased_to->facnum); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->msb); + lxt2_wr_emit_u32z(lt, lt->sorted_facs[i]->lsb); + lxt2_wr_emit_u32z(lt, LXT2_WR_SYM_F_ALIAS); + } + } + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + lt->break_header_size = lt->position; /* in case we need to emit multiple lxt2s with same header */ + lt->zfacgeometry_size = lt->position - lt->facgeometry_offset; + + fseeko(lt->handle, lt->facname_offset + 12, SEEK_SET); + lxt2_wr_emit_u32(lt, lt->zfacname_size); /* backpatch sizes... */ + lxt2_wr_emit_u32(lt, lt->zfacname_predec_size); + lxt2_wr_emit_u32(lt, lt->zfacgeometry_size); + + lt->numfacs = facs_encountered; /* don't process alias value changes ever */ + } + + free(aliascache); + } +} + + +/* + * initialize the trace and get back an lt context + */ +struct lxt2_wr_trace *lxt2_wr_init(const char *name) +{ +struct lxt2_wr_trace *lt=(struct lxt2_wr_trace *)calloc(1, sizeof(struct lxt2_wr_trace)); + +if((!name)||(!(lt->handle=fopen(name, "wb")))) + { + free(lt); + lt=NULL; + } + else + { + lt->lxtname = strdup(name); + + lxt2_wr_emit_u16(lt, LXT2_WR_HDRID); + lxt2_wr_emit_u16(lt, LXT2_WR_VERSION); + lxt2_wr_emit_u8 (lt, LXT2_WR_GRANULE_SIZE); /* currently 32 or 64 */ + lt->timescale = -9; + lt->maxgranule = LXT2_WR_GRANULE_NUM; + lxt2_wr_set_compression_depth(lt, 4); /* set fast/loose compression depth, user can fix this any time after init */ + lt->initial_value = 'x'; + } + +return(lt); +} + + +/* + * setting break size + */ +void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz) +{ +if(lt) + { + lt->break_size = siz; + } +} + + +/* + * enable/disable partial dump mode (for faster reads) + */ +void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->partial = 0; + lt->partial_zip = 0; + } +} + +void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode) +{ +if(lt) + { + lt->partial = 1; + lt->partial_zip = (zipmode != 0); + lt->partial_iter = LXT2_WR_PARTIAL_SIZE; + } +} + +void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name) +{ +struct lxt2_wr_symbol *s; + +if((lt)&&(name)&&(!lt->sorted_facs)) + { + s=lxt2_wr_symfind(lt, name); + if(s) + { + while(s->aliased_to) /* find root alias */ + { + s=s->aliased_to; + } + + s->partial_preference = 1; + } + } +} + + +/* + * enable/disable checkpointing (for smaller files) + */ +void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->no_checkpoint = 1; + } +} + +void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt) +{ +if(lt) + { + lt->no_checkpoint = 0; + } +} + + +/* + * set initial value of trace (0, 1, x, z) only legal vals + */ +void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value) +{ +if(lt) + { + switch(value) + { + case '0': + case '1': + case 'x': + case 'z': break; + case 'Z': value = 'z'; break; + default: value = 'x'; break; + } + + lt->initial_value = value; + } +} + + +/* + * maint function for finding a symbol if it exists + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name) +{ +struct lxt2_wr_symbol *s=NULL; + +if((lt)&&(name)) s=lxt2_wr_symfind(lt, name); +return(s); +} + + +/* + * add a trace (if it doesn't exist already) + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags) +{ +struct lxt2_wr_symbol *s; +int len; +int flagcnt; + +if((!lt)||(lt->sorted_facs)) return(NULL); + +flagcnt = ((flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((flags&LXT2_WR_SYM_F_STRING)!=0); + +if((flagcnt>1)||(!lt)||(!name)||(lxt2_wr_symfind(lt, name))) return (NULL); + +s=lxt2_wr_symadd(lt, name, lxt2_wr_hash(name)); +s->rows = rows; +s->flags = flags&(~LXT2_WR_SYM_F_ALIAS); /* aliasing makes no sense here.. */ + +if(!flagcnt) + { + s->msb = msb; + s->lsb = lsb; + s->len = (msbvalue = strdup("NaN"); + } + else + { + if(flags & LXT2_WR_SYM_F_INTEGER) + { + s->len = 32; + } + + s->value = malloc(s->len + 1); + memset(s->value, lt->initial_value, s->len); + s->value[s->len]=0; + + s->msk = LXT2_WR_GRAN_1VAL; /* stuff in an initial value */ + switch(lt->initial_value) + { + case '0': s->chg[0] = LXT2_WR_ENC_0; break; + case '1': s->chg[0] = LXT2_WR_ENC_1; break; + case 'z': s->chg[0] = LXT2_WR_ENC_Z; break; + default: s->chg[0] = LXT2_WR_ENC_X; break; + } + s->chgpos++; /* don't worry that a time doesn't exist as it will soon enough.. */ + } + +s->symchain = lt->symchain; +lt->symchain = s; + +lt->numfacs++; + +if((len=strlen(name)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(s); +} + +/* + * add an alias trace (if it doesn't exist already and orig is found) + */ +struct lxt2_wr_symbol *lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb) +{ +struct lxt2_wr_symbol *s, *sa; +int len; +int bitlen; +int flagcnt; + +if((!lt)||(!existing_name)||(!alias)||(!(s=lxt2_wr_symfind(lt, existing_name)))||(lxt2_wr_symfind(lt, alias))) return (NULL); + +if(lt->sorted_facs) return(NULL); + +while(s->aliased_to) /* find root alias */ + { + s=s->aliased_to; + } + +flagcnt = ((s->flags&LXT2_WR_SYM_F_INTEGER)!=0) + ((s->flags&LXT2_WR_SYM_F_DOUBLE)!=0) + ((s->flags&LXT2_WR_SYM_F_STRING)!=0); +bitlen = (msblen)) return(NULL); + +sa=lxt2_wr_symadd(lt, alias, lxt2_wr_hash(alias)); +sa->flags = LXT2_WR_SYM_F_ALIAS; /* only point this can get set */ +sa->aliased_to = s; + +if(!flagcnt) + { + sa->msb = msb; + sa->lsb = lsb; + sa->len = bitlen; + } + +sa->symchain = lt->symchain; +lt->symchain = sa; +lt->numfacs++; +lt->numalias++; +if((len=strlen(alias)) > lt->longestname) lt->longestname = len; +lt->numfacbytes += (len+1); + +return(sa); +} + + +/* + * set current time/granule updating + */ +int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval) +{ +return(lxt2_wr_set_time64(lt, lt->maxtime + (lxttime_t)timeval)); +} + +int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval) +{ +return(lxt2_wr_set_time64(lt, (lxttime_t)timeval)); +} + +int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval) +{ +return(lxt2_wr_set_time64(lt, lt->maxtime + timeval)); +} + + +/* + * file size limiting/header cloning... + */ +static void lxt2_wr_emit_do_breakfile(struct lxt2_wr_trace *lt) +{ +unsigned int len = strlen(lt->lxtname); +int i; +char *tname = malloc(len + 30); +FILE *f2, *clone; +off_t cnt, seg; +char buf[32768]; + +for(i=len;i>0;i--) + { + if(lt->lxtname[i]=='.') break; + } + +if(!i) + { + sprintf(tname, "%s_%03d.lxt", lt->lxtname, ++lt->break_number); + } + else + { + memcpy(tname, lt->lxtname, i); + sprintf(tname+i, "_%03d.lxt", ++lt->break_number); + } + +f2 = fopen(tname, "wb"); +if(!f2) /* if error, keep writing to same output file...sorry */ + { + free(tname); + return; + } + +clone = fopen(lt->lxtname, "rb"); +if(!clone) + { /* this should never happen */ + fclose(f2); + unlink(tname); + free(tname); + return; + } + +/* clone original header */ +for(cnt = 0; cnt < lt->break_header_size; cnt += sizeof(buf)) + { + seg = lt->break_header_size - cnt; + if(seg > sizeof(buf)) + { + seg = sizeof(buf); + } + + if(fread(buf, seg, 1, clone)) + { + if(!fwrite(buf, seg, 1, f2)) break; /* write error! */ + } + } + +fclose(clone); +fclose(lt->handle); +lt->handle = f2; +free(tname); +} + + +/* + * emit granule + */ +void lxt2_wr_flush_granule(struct lxt2_wr_trace *lt, int do_finalize) +{ +unsigned int idx_nbytes, map_nbytes, i, j; +struct lxt2_wr_symbol *s; +unsigned int partial_iter; +unsigned int iter, iter_hi; +unsigned char using_partial, using_partial_zip=0; +off_t current_iter_pos=0; +int early_flush; + +if(lt->flush_valid) + { + if(lt->flushtime == lt->lasttime) + { + return; + } + + lt->flush_valid = 0; + } + +lt->granule_dirty = 0; + +if((using_partial=(lt->partial)&&(lt->numfacs>lt->partial_iter))) + { + partial_iter = lt->partial_iter; + using_partial_zip = lt->partial_zip; + } + else + { + partial_iter = lt->numfacs; + } + +if(!lt->timegranule) + { + int attempt_break_state = 2; + + do { + fseeko(lt->handle, 0L, SEEK_END); + lt->current_chunk=lt->position = ftello(lt->handle); + + if((lt->break_size)&&(attempt_break_state==2)&&(lt->position >= lt->break_size)&&(lt->position != lt->break_header_size)) + { + lxt2_wr_emit_do_breakfile(lt); + attempt_break_state--; + } + else + { + attempt_break_state = 0; + } + } while(attempt_break_state); + + /* fprintf(stderr, "First chunk position is %d (0x%08x)\n", lt->current_chunk, lt->current_chunk); */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u64(lt, 0, 0); /* begin time of section */ + lxt2_wr_emit_u64(lt, 0, 0); /* end time of section */ + fflush(lt->handle); + lt->current_chunkz = lt->position; + /* fprintf(stderr, "First chunkz position is %d (0x%08x)\n", lt->current_chunkz, lt->current_chunkz); */ + + if(!using_partial_zip) + { + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + } + else + { + lt->zpackcount_cumulative = 0; + } + lt->zpackcount = 0; + } + + + +for(iter=0; iternumfacs; iter=iter_hi) +{ +unsigned int total_chgs; +unsigned int partial_length; + +total_chgs = 0; +partial_length = 0; + +iter_hi = iter + partial_iter; +if(iter_hi > lt->numfacs) iter_hi = lt->numfacs; + +for(j=iter;jsorted_facs[j]->msk; + + lt->mapdict = lxt2_wr_ds_splay (msk, lt->mapdict); + if((!lt->mapdict)||(lt->mapdict->item != msk)) + { + lt->mapdict = lxt2_wr_ds_insert(msk, lt->mapdict, lt->num_map_entries); + lt->num_map_entries++; + + if(lt->mapdict_curr) + { + lt->mapdict_curr->next = lt->mapdict; + lt->mapdict_curr = lt->mapdict; + } + else + { + lt->mapdict_head = lt->mapdict_curr = lt->mapdict; + } + } + } + +if(lt->num_map_entries <= 256) { map_nbytes = 1; } +else if(lt->num_map_entries <= 256*256) { map_nbytes = 2; } +else if(lt->num_map_entries <= 256*256*256) { map_nbytes = 3; } +else { map_nbytes = 4; } + +if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256) { idx_nbytes = 1; } +else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256) { idx_nbytes = 2; } +else if((lt->num_dict_entries+LXT2_WR_DICT_START) <= 256*256*256) { idx_nbytes = 3; } +else { idx_nbytes = 4; } + +if(using_partial) + { + /* skip */ + partial_length = 1 + /* lt->timepos */ + lt->timepos * sizeof(lxttime_t)+ /* timevals */ + + 1 + /* map_nbytes */ + (iter_hi-iter) * map_nbytes + /* actual map */ + 1; /* idx_nbytes */ + + for(j=iter;jsorted_facs[j]; + total_chgs += s->chgpos; + } + total_chgs *= idx_nbytes; /* vch skip */ + + partial_length += total_chgs; /* actual changes */ + + if(using_partial_zip) + { + fseeko(lt->handle, 0L, SEEK_END); + current_iter_pos = ftello(lt->handle); + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u32(lt, partial_length+9); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, iter); /* begin iter of section */ + fflush(lt->handle); + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + lt->zpackcount = 0; + } + + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME_PARTIAL); + lxt2_wr_emit_u32z(lt, iter); + lxt2_wr_emit_u32z(lt, partial_length); + } + else + { + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_TIME); + } + +lxt2_wr_emit_u8z(lt, lt->timepos); +for(i=0;itimepos;i++) + { + lxt2_wr_emit_u64z(lt, (lt->timetable[i]>>32)&0xffffffff, lt->timetable[i]&0xffffffff); + } +gzflush_buffered(lt, 0); + + +lxt2_wr_emit_u8z(lt, map_nbytes); +for(j=iter;jsorted_facs[j]; + lt->mapdict = lxt2_wr_ds_splay (s->msk, lt->mapdict); + val = lt->mapdict->val; + + switch(map_nbytes) + { + case 1: lxt2_wr_emit_u8z(lt, val); break; + case 2: lxt2_wr_emit_u16z(lt, val); break; + case 3: lxt2_wr_emit_u24z(lt, val); break; + case 4: lxt2_wr_emit_u32z(lt, val); break; + } + + s->msk = LXT2_WR_GRAN_0VAL; + } + + +lxt2_wr_emit_u8z(lt, idx_nbytes); +gzflush_buffered(lt, 0); +for(j=iter;jsorted_facs[j]; + + for(i=0;ichgpos;i++) + { + switch(idx_nbytes) + { + case 1: lxt2_wr_emit_u8z (lt, s->chg[i]); break; + case 2: lxt2_wr_emit_u16z(lt, s->chg[i]); break; + case 3: lxt2_wr_emit_u24z(lt, s->chg[i]); break; + case 4: lxt2_wr_emit_u32z(lt, s->chg[i]); break; + } + } + + s->chgpos = 0; + } + +if(using_partial_zip) + { + off_t clen; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + + clen = lt->position - current_iter_pos - 12; + fseeko(lt->handle, current_iter_pos, SEEK_SET); + + lt->zpackcount_cumulative+=lt->zpackcount; + + lxt2_wr_emit_u32(lt, clen); + } + else + { + gzflush_buffered(lt, 0); + } +} /* ...for(iter) */ + + +lt->timepos = 0; +lt->timegranule++; + +if(lt->break_size) + { + early_flush = (ftello(lt->handle) >= lt->break_size); + } + else + { + early_flush = 0; + } + +if((lt->timegranule>=lt->maxgranule)||(do_finalize)||(early_flush)) + { + off_t unclen, clen; + lxt2_wr_ds_Tree *dt, *dt2; + lxt2_wr_dslxt_Tree *ds, *ds2; + + if(using_partial_zip) + { + fseeko(lt->handle, 0L, SEEK_END); + current_iter_pos = ftello(lt->handle); + lxt2_wr_emit_u32(lt, 0); /* size of this section (compressed) */ + lxt2_wr_emit_u32(lt, 0); /* size of this section (uncompressed) */ + lxt2_wr_emit_u32(lt, ~0); /* control section */ + fflush(lt->handle); + + lt->zhandle = gzdopen(dup(fileno(lt->handle)), lt->zmode); + lt->zpackcount = 0; + } + + /* fprintf(stderr, "reached granule %d, finalizing block for section %d\n", lt->timegranule, lt->numsections); */ + lt->numsections++; + + /* finalize string dictionary */ + lxt2_wr_emit_u8z(lt, LXT2_WR_GRAN_SECT_DICT); + + ds = lt->dict_head; + /* fprintf(stderr, "num_dict_entries: %d\n", lt->num_dict_entries); */ + gzflush_buffered(lt, 0); + for(i=0;inum_dict_entries;i++) + { + /* fprintf(stderr, "%8d %8d) '%s'\n", ds->val, i, ds->item); */ + if(ds->val != i) + { + fprintf(stderr, "internal error line %d\n", __LINE__); + exit(255); + } + + lxt2_wr_emit_stringz(lt, ds->item); + ds2 = ds->next; + free(ds->item); + free(ds); + ds = ds2; + } + lt->dict_head = lt->dict_curr = lt->dict = NULL; + + /* finalize map dictionary */ + dt = lt->mapdict_head; + /* fprintf(stderr, "num_map_entries: %d\n", lt->num_map_entries); */ + gzflush_buffered(lt, 0); + for(i=0;inum_map_entries;i++) + { + /* fprintf(stderr, "+++ %08x (%d)(%d)\n", dt->item, i, dt->val); */ + if(dt->val != i) + { + fprintf(stderr, "internal error line %d\n", __LINE__); + exit(255); + } + +#if LXT2_WR_GRANULE_SIZE > 32 + lxt2_wr_emit_u64z(lt, (dt->item>>32)&0xffffffff, dt->item&0xffffffff); +#else + lxt2_wr_emit_u32z(lt, dt->item); +#endif + + dt2 = dt->next; + free(dt); + dt = dt2; + } + lt->mapdict_head = lt->mapdict_curr = lt->mapdict = NULL; + + lxt2_wr_emit_u32z(lt, lt->num_dict_entries); /* -12 */ + lxt2_wr_emit_u32z(lt, lt->dict_string_mem_required); /* -8 */ + lxt2_wr_emit_u32z(lt, lt->num_map_entries); /* -4 */ + + lt->num_map_entries = 0; + lt->num_dict_entries = lt->dict_string_mem_required = 0; + + /* fprintf(stderr, "returned from finalize..\n"); */ + + if(using_partial_zip) + { + off_t c_len; + + gzflush_buffered(lt, 1); + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + + c_len = lt->position - current_iter_pos - 12; + fseeko(lt->handle, current_iter_pos, SEEK_SET); + + lt->zpackcount_cumulative+=lt->zpackcount; + lxt2_wr_emit_u32(lt, c_len); + lxt2_wr_emit_u32(lt, lt->zpackcount); + } + else + { + gzflush_buffered(lt, 1); + } + + fseeko(lt->handle, 0L, SEEK_END); + lt->position=ftello(lt->handle); + /* fprintf(stderr, "file position after dumping dict: %d 0x%08x\n", lt->position, lt->position); */ + + unclen = lt->zpackcount; + clen = lt->position - lt->current_chunkz; + + /* fprintf(stderr, "%d/%d un/compressed bytes in section\n", unclen, clen); */ + + fseeko(lt->handle, lt->current_chunk, SEEK_SET); + if(using_partial_zip) + { + lxt2_wr_emit_u32(lt, lt->zpackcount_cumulative); + lxt2_wr_emit_u32(lt, clen); + } + else + { + lxt2_wr_emit_u32(lt, unclen); + lxt2_wr_emit_u32(lt, clen); + } + lxt2_wr_emit_u64(lt, (lt->firsttime>>32)&0xffffffff, lt->firsttime&0xffffffff); + lxt2_wr_emit_u64(lt, (lt->lasttime>>32)&0xffffffff, lt->lasttime&0xffffffff); + + /* fprintf(stderr, "start: %lld, end %lld\n", lt->firsttime, lt->lasttime); */ + + lt->timegranule=0; + lt->numblock++; + } + +if(do_finalize) + { + lt->flush_valid = 1; + lt->flushtime = lt->lasttime; + } +} + + +int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval) +{ +int rc=0; + +if(lt) + { + if(lt->timeset) + { + if(timeval > lt->maxtime) + { + if(lt->bumptime) + { + lt->bumptime = 0; + + if(!lt->flush_valid) + { + lt->timepos++; + } + else + { + lt->flush_valid = 0; + } + + if(lt->timepos == LXT2_WR_GRANULE_SIZE) + { + /* fprintf(stderr, "flushing granule to disk at time %d\n", (unsigned int)timeval); */ + lxt2_wr_flush_granule(lt, 0); + } + } + + /* fprintf(stderr, "updating time to %d (%d dict entries/%d bytes)\n", (unsigned int)timeval, lt->num_dict_entries, lt->dict_string_mem_required); */ + lt->timetable[lt->timepos] = timeval; + lt->lasttime = timeval; + } + } + else + { + lt->timeset = 1; + lt->mintime = lt->maxtime = timeval; + + lt->timetable[lt->timepos] = timeval; + } + + if( (!lt->timepos) && (!lt->timegranule) ) + { + lt->firsttime = timeval; + lt->lasttime = timeval; + } + + if( (!lt->timepos) && (!lt->timegranule) && ((!lt->numblock)||(!lt->no_checkpoint)) ) + { + struct lxt2_wr_symbol *s = lt->symchain; + + /* fprintf(stderr, "initial value burst timepos==0, timegranule==0\n"); */ + if(lt->blackout) + { + lt->blackout = 0; + lxt2_wr_set_dumpoff(lt); + } + else + { + s = lt->symchain; + while(s) + { + if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) + { + if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + lxt2_wr_emit_value_bit_string(lt, s, 0, s->value); + } + else if (s->flags&LXT2_WR_SYM_F_DOUBLE) + { + double value = 0; + + sscanf(s->value, "%lg", &value); + errno = 0; + lxt2_wr_emit_value_double(lt, s, 0, value); + } + else if (s->flags&LXT2_WR_SYM_F_STRING) + { + lxt2_wr_emit_value_string(lt, s, 0, s->value); + } + } + s=s->symchain; + } + } + /* fprintf(stderr, "done initial value burst timepos==0, timegranule==0\n"); */ + } + + lt->granule_dirty = 1; + rc = 1; + } + +return(rc); +} + + +/* + * sets trace timescale as 10**x seconds + */ +void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale) +{ +if(lt) + { + lt->timescale = timescale; + } +} + + +/* + * set number of granules per section + * (can modify dynamically) + */ +void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule) +{ +if(lt) + { + if(!maxgranule) maxgranule = ~0; + lt->maxgranule = maxgranule; + } +} + + +/* + * Sets bracket stripping (useful for VCD conversions of + * bitblasted nets) + */ +void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit) +{ +if(lt) + { + lt->do_strip_brackets = (doit!=0); + } +} + + + +static char *lxt2_wr_expand_integer_to_bits(unsigned int len, int value) +{ +static char s[33]; +char *p = s; +unsigned int i; + +if(len>32) len=32; + +len--; + +for(i=0;i<=len;i++) + { + *(p++) = '0' | ((value & (1<<(len-i)))!=0); + } +*p = 0; + +return(s); +} + + +int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); + +return(lxt2_wr_emit_value_bit_string(lt, s, row, lxt2_wr_expand_integer_to_bits(s->len, value))); +} + + + +int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(s->flags&LXT2_WR_SYM_F_DOUBLE) + { + char d_buf[32]; + unsigned int idx; + + rc = 1; + sprintf(d_buf, "%.16g", value); + if(!strcmp(d_buf, s->value)) return(rc); + + lt->bumptime = 1; + free(s->value); + s->value = strdup(d_buf); + + lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(d_buf)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, d_buf); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + lt->granule_dirty = 1; + } + +return(rc); +} + + +int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) +{ +int rc=0; + +if((!lt)||(lt->blackout)||(!s)||(!value)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +if(s->flags&LXT2_WR_SYM_F_STRING) + { + unsigned int idx; + + rc = 1; + if(!strcmp(value, s->value)) return(rc); + + lt->bumptime = 1; + free(s->value); + s->value = strdup(value); + + lt->dict = lxt2_wr_dslxt_splay (s->value, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(value)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, value); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + lt->granule_dirty = 1; + } + +return(rc); +} + + +int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value) +{ +int rc=0; +char *vpnt; +char *vfix; +int valuelen; +int i; + +if((!lt)||(lt->blackout)||(!s)||(!value)||(!*value)||(row)) return(rc); + +if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + +while(s->aliased_to) /* find root alias if exists */ + { + s=s->aliased_to; + } + +valuelen = strlen(value); /* ensure string is proper length */ +if(valuelen == s->len) + { + vfix = wave_alloca(s->len+1); + strcpy(vfix, value); + value = vfix; + } + else + { + vfix = wave_alloca(s->len+1); + + if(valuelen < s->len) + { + int lendelta = s->len - valuelen; + memset(vfix, (value[0]!='1') ? value[0] : '0', lendelta); + strcpy(vfix+lendelta, value); + } + else + { + memcpy(vfix, value, s->len); + vfix[s->len] = 0; + } + + value = vfix; + } + +for(i=0;ilen;i++) + { + unsigned char ch = value[i]; + if((ch>='A')&&(ch<='Z')) value[i] = ch + ('a'-'A'); + } + +if ( (lt->timepos || lt->timegranule) && !strcmp(value, s->value) ) + { + return(1); /* redundant value change */ + } + + +if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + char prevch; + int idx; + + lt->bumptime = 1; + + vpnt = value; + prevch = *vpnt; + while(*vpnt) + { + if(prevch == *vpnt) + { + vpnt++; + } + else + { + prevch = 0; + break; + } + } + + switch(prevch) + { + case '0': idx = LXT2_WR_ENC_0; break; + case '1': idx = LXT2_WR_ENC_1; break; + case 'X': + case 'x': idx = LXT2_WR_ENC_X; break; + case 'Z': + case 'z': idx = LXT2_WR_ENC_Z; break; + default: idx = -1; break; + } + + if((lt->timepos)||(lt->timegranule)) + { + for(i=0;ilen;i++) + { + char ch = value[i]; + + switch(ch) + { + case '0': if(s->value[i]!='1') goto nextalg; else break; + case '1': if(s->value[i]!='0') goto nextalg; else break; + default: goto nextalg; + } + } + idx = LXT2_WR_ENC_INV; goto do_enc; + + nextalg: + if(s->len > 1) + { + if(!memcmp(s->value+1, value, s->len-1)) + { + if((value[s->len-1]&0xfe)=='0') + { + idx = LXT2_WR_ENC_LSH0 + (value[s->len-1]&0x01); + goto do_enc; + } + } + else + if(!memcmp(s->value, value+1, s->len-1)) + { + if((value[0]&0xfe)=='0') + { + idx = LXT2_WR_ENC_RSH0 + (value[0]&0x01); + goto do_enc; + } + } + + if(s->len <= 32) + { + unsigned int intval_old = 0, intval_new = 0; + unsigned int msk; + + for(i=0;ilen;i++) + { + char ch = value[i]; + if((ch!='0')&&(ch!='1')) goto idxchk; + intval_new <<= 1; + intval_new |= ((unsigned int)(ch&1)); + + ch = s->value[i]; + if((ch!='0')&&(ch!='1')) goto idxchk; + intval_old <<= 1; + intval_old |= ((unsigned int)(ch&1)); + } + + msk = (~0)>>(32-s->len); + if( ((intval_old+1)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD1; goto do_enc; } + if( ((intval_old-1)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB1; goto do_enc; } + + if( ((intval_old+2)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD2; goto do_enc; } + if( ((intval_old-2)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB2; goto do_enc; } + + if( ((intval_old+3)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD3; goto do_enc; } + if( ((intval_old-3)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB3; goto do_enc; } + + if(s->len > 2) + { + if( ((intval_old+4)&msk) == intval_new ) { idx = LXT2_WR_ENC_ADD4; goto do_enc; } + if( ((intval_old-4)&msk) == intval_new ) { idx = LXT2_WR_ENC_SUB4; goto do_enc; } + } + + } + } + } + + +idxchk: if(idx<0) + { + vpnt = lxt2_wr_vcd_truncate_bitvec(value); + lt->dict = lxt2_wr_dslxt_splay (vpnt, lt->dict); + + if(!lxt2_wr_dslxt_success) + { + unsigned int vlen = strlen(vpnt)+1; + char *vcopy = (char *)malloc(vlen); + strcpy(vcopy, vpnt); + lt->dict_string_mem_required += vlen; + lt->dict = lxt2_wr_dslxt_insert(vcopy, lt->dict, lt->num_dict_entries); + + if(lt->dict_curr) + { + lt->dict_curr->next = lt->dict; + lt->dict_curr = lt->dict; + } + else + { + lt->dict_head = lt->dict_curr = lt->dict; + } + + idx = lt->num_dict_entries + LXT2_WR_DICT_START; + lt->num_dict_entries++; + } + else + { + idx = lt->dict->val + LXT2_WR_DICT_START; + } + } + +do_enc: + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = idx; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = idx; + } + + strncpy(s->value, value, s->len); + + lt->granule_dirty = 1; + } + +return(rc); +} + + +/* + * dumping control + */ +void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt) +{ +struct lxt2_wr_symbol *s; + +if((lt)&&(!lt->blackout)) + { + if(!lt->emitted) + { + lxt2_wr_emitfacs(lt); + lt->emitted = 1; + + if(!lt->timeset) + { + lxt2_wr_set_time(lt, 0); + } + } + + s = lt->symchain; + while(s) + { + if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) + { + if((s->msk & (LXT2_WR_GRAN_1VAL<timepos)) == LXT2_WR_GRAN_0VAL) + { + s->msk |= (LXT2_WR_GRAN_1VAL<timepos); + s->chg[s->chgpos] = LXT2_WR_ENC_BLACKOUT; + + s->chgpos++; + } + else + { + s->chg[s->chgpos-1] = LXT2_WR_ENC_BLACKOUT; + } + } + + s=s->symchain; + } + + lt->bumptime = 1; + lt->blackout = 1; + lt->granule_dirty = 1; + } +} + + +void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt) +{ +int i; +struct lxt2_wr_symbol *s; + +if((lt)&&(lt->blackout)) + { + lt->blackout = 0; + + s = lt->symchain; + while(s) + { + if(!(s->flags&LXT2_WR_SYM_F_ALIAS)) + { + if(s->flags&LXT2_WR_SYM_F_DOUBLE) + { + free(s->value); + s->value = strdup("0"); /* will cause mismatch then flush */ + } + else + { + if(!(s->flags&LXT2_WR_SYM_F_STRING)) + { + s->value[0] = '-'; /* will cause mismatch then flush */ + for(i=1;ilen;i++) + { + s->value[i] = 'x'; /* initial value */ + } + s->value[i]=0; + } + else + { + free(s->value); + s->value = calloc(1, 1*sizeof(char)); + } + } + } + + s=s->symchain; + } + + s = lt->symchain; + while(s) + { + if((!(s->flags&LXT2_WR_SYM_F_ALIAS))&&(s->rows<2)) + { + if(!(s->flags&(LXT2_WR_SYM_F_DOUBLE|LXT2_WR_SYM_F_STRING))) + { + lxt2_wr_emit_value_bit_string(lt, s, 0, "x"); + } + else if (s->flags&LXT2_WR_SYM_F_DOUBLE) + { + double value; + sscanf("NaN", "%lg", &value); + lxt2_wr_emit_value_double(lt, s, 0, value); + } + else if (s->flags&LXT2_WR_SYM_F_STRING) + { + lxt2_wr_emit_value_string(lt, s, 0, "UNDEF"); + } + } + s=s->symchain; + } + } +} + + +/* + * flush the trace... + */ +void lxt2_wr_flush(struct lxt2_wr_trace *lt) +{ +if(lt) + { + if((lt->timegranule)||(lt->timepos > 0)) + { + if(lt->granule_dirty) + { + lt->timepos++; + lxt2_wr_flush_granule(lt, 1); + } + } + } +} + + +/* + * close out the trace and fixate it + */ +void lxt2_wr_close(struct lxt2_wr_trace *lt) +{ +if(lt) + { + if(lt->granule_dirty) + { + lt->timepos++; + lxt2_wr_flush_granule(lt, 1); + } + + if(lt->symchain) + { + struct lxt2_wr_symbol *s = lt->symchain; + struct lxt2_wr_symbol *s2; + + while(s) + { + free(s->name); + free(s->value); + s2=s->symchain; + free(s); + s=s2; + } + + lt->symchain=NULL; + } + + free(lt->lxtname); + free(lt->sorted_facs); + fclose(lt->handle); + free(lt); + } + +} + +/* + * set compression depth + */ +void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth) +{ +if(lt) + { + if(depth > 9) depth = 9; + sprintf(lt->zmode, "wb%d", depth); + } +} + +/* + * $Id: lxt2_write.c,v 1.3 2010/05/03 20:11:03 gtkwave Exp $ + * $Log: lxt2_write.c,v $ + * Revision 1.3 2010/05/03 20:11:03 gtkwave + * cppcheck warning fixes + * + * Revision 1.2 2008/12/20 05:08:26 gtkwave + * -Wshadow warning cleanups + * + * Revision 1.1.1.1 2007/05/30 04:28:21 gtkwave + * Imported sources + * + * Revision 1.2 2007/04/20 02:08:18 gtkwave + * initial release + * + */ diff --git a/src/spicelib/analysis/lxt2_write.h b/src/spicelib/analysis/lxt2_write.h new file mode 100644 index 000000000..dc11ade87 --- /dev/null +++ b/src/spicelib/analysis/lxt2_write.h @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2003-2010 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DEFS_LXTW_H +#define DEFS_LXTW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WAVE_ALLOCA_H +#define WAVE_ALLOCA_H +#include +#include +#define wave_alloca alloca +#define LXT2_WR_HDRID (0x1380) +#define LXT2_WR_VERSION (0x0001) + +#define LXT2_WR_GRANULE_SIZE (64) +#define LXT2_WR_GRANULE_NUM (256) +#define LXT2_WR_PARTIAL_SIZE (2048) + +#define LXT2_WR_GRAN_SECT_TIME 0 +#define LXT2_WR_GRAN_SECT_DICT 1 +#define LXT2_WR_GRAN_SECT_TIME_PARTIAL 2 + +#define LXT2_WR_GZWRITE_BUFFER 4096 +#define LXT2_WR_SYMPRIME 500009 + +typedef uint64_t lxttime_t; + + +#define LXT2_WR_LLD "%lld" +#define LXT2_WR_LLDESC(x) x##LL +#define LXT2_WR_ULLDESC(x) x##ULL + +#if LXT2_WR_GRANULE_SIZE > 32 +typedef unsigned long long granmsk_t; +#define LXT2_WR_GRAN_0VAL (LXT2_WR_ULLDESC(0)) +#define LXT2_WR_GRAN_1VAL (LXT2_WR_ULLDESC(1)) +#else +typedef unsigned int granmsk_t; +#define LXT2_WR_GRAN_0VAL (0) +#define LXT2_WR_GRAN_1VAL (1) +#endif + + +enum LXT2_WR_Encodings { + LXT2_WR_ENC_0, + LXT2_WR_ENC_1, + LXT2_WR_ENC_INV, + LXT2_WR_ENC_LSH0, + LXT2_WR_ENC_LSH1, + LXT2_WR_ENC_RSH0, + LXT2_WR_ENC_RSH1, + + LXT2_WR_ENC_ADD1, + LXT2_WR_ENC_ADD2, + LXT2_WR_ENC_ADD3, + LXT2_WR_ENC_ADD4, + + LXT2_WR_ENC_SUB1, + LXT2_WR_ENC_SUB2, + LXT2_WR_ENC_SUB3, + LXT2_WR_ENC_SUB4, + + LXT2_WR_ENC_X, + LXT2_WR_ENC_Z, + + LXT2_WR_ENC_BLACKOUT, + + LXT2_WR_DICT_START + }; + +/* + * integer splay + */ +typedef struct lxt2_wr_ds_tree_node lxt2_wr_ds_Tree; +struct lxt2_wr_ds_tree_node { + lxt2_wr_ds_Tree * left, * right; + granmsk_t item; + int val; + lxt2_wr_ds_Tree * next; +}; + + +/* + * string splay + */ +typedef struct lxt2_wr_dslxt_tree_node lxt2_wr_dslxt_Tree; +struct lxt2_wr_dslxt_tree_node { + lxt2_wr_dslxt_Tree * left, * right; + char *item; + unsigned int val; + lxt2_wr_dslxt_Tree * next; +}; + + +struct lxt2_wr_trace +{ +FILE *handle; +gzFile zhandle; + +lxt2_wr_dslxt_Tree *dict; /* dictionary manipulation */ +unsigned int num_dict_entries; +unsigned int dict_string_mem_required; +lxt2_wr_dslxt_Tree *dict_head; +lxt2_wr_dslxt_Tree *dict_curr; + +lxt2_wr_ds_Tree *mapdict; /* bitmap compression */ +unsigned int num_map_entries; +lxt2_wr_ds_Tree *mapdict_head; +lxt2_wr_ds_Tree *mapdict_curr; + +off_t position; +off_t zfacname_predec_size, zfacname_size, zfacgeometry_size; +off_t zpackcount, zpackcount_cumulative; +off_t current_chunk, current_chunkz; + +struct lxt2_wr_symbol *sym[LXT2_WR_SYMPRIME]; +struct lxt2_wr_symbol **sorted_facs; +struct lxt2_wr_symbol *symchain; +int numfacs, numalias; +int numfacbytes; +int longestname; + +int numsections, numblock; +off_t facname_offset, facgeometry_offset; + +lxttime_t mintime, maxtime; +unsigned int timegranule; +int timescale; +int timepos; +unsigned int maxgranule; +lxttime_t firsttime, lasttime; +lxttime_t timetable[LXT2_WR_GRANULE_SIZE]; + +unsigned int partial_iter; + +char *compress_fac_str; +int compress_fac_len; + +lxttime_t flushtime; +unsigned flush_valid : 1; + +unsigned do_strip_brackets : 1; +unsigned emitted : 1; /* gate off change field zmode changes when set */ +unsigned timeset : 1; /* time has been modified from 0..0 */ +unsigned bumptime : 1; /* says that must go to next time position in granule as value change exists for current time */ +unsigned granule_dirty : 1; /* for flushing out final block */ +unsigned blackout : 1; /* blackout on/off */ +unsigned partial : 1; /* partial (vertical) trace support */ +unsigned partial_zip : 1; /* partial (vertical) trace support for zip subregions */ +unsigned no_checkpoint : 1; /* turns off interblock checkpointing */ +unsigned partial_preference : 1; /* partial preference encountered on some facs */ + +char initial_value; + +char zmode[4]; /* fills in with "wb0".."wb9" */ +unsigned int gzbufpnt; +unsigned char gzdest[LXT2_WR_GZWRITE_BUFFER + 4]; /* enough for zlib buffering */ + +char *lxtname; +off_t break_size; +off_t break_header_size; +unsigned int break_number; +}; + + +struct lxt2_wr_symbol +{ +struct lxt2_wr_symbol *next; +struct lxt2_wr_symbol *symchain; +char *name; +int namlen; + +int facnum; +struct lxt2_wr_symbol *aliased_to; + +char *value; /* fac's actual value */ + +unsigned int rows; +int msb, lsb; +int len; +int flags; + +unsigned partial_preference : 1; /* in order to shove nets to the first partial group */ + +unsigned int chgpos; +granmsk_t msk; /* must contain LXT2_WR_GRANULE_SIZE bits! */ +unsigned int chg[LXT2_WR_GRANULE_SIZE]; +}; + + +#define LXT2_WR_SYM_F_BITS (0) +#define LXT2_WR_SYM_F_INTEGER (1<<0) +#define LXT2_WR_SYM_F_DOUBLE (1<<1) +#define LXT2_WR_SYM_F_STRING (1<<2) +#define LXT2_WR_SYM_F_TIME (LXT2_WR_SYM_F_STRING) /* user must correctly format this as a string */ +#define LXT2_WR_SYM_F_ALIAS (1<<3) + +#define LXT2_WR_SYM_F_SIGNED (1<<4) +#define LXT2_WR_SYM_F_BOOLEAN (1<<5) +#define LXT2_WR_SYM_F_NATURAL ((1<<6)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_POSITIVE ((1<<7)|(LXT2_WR_SYM_F_INTEGER)) +#define LXT2_WR_SYM_F_CHARACTER (1<<8) + +#define LXT2_WR_SYM_F_CONSTANT (1<<9) +#define LXT2_WR_SYM_F_VARIABLE (1<<10) +#define LXT2_WR_SYM_F_SIGNAL (1<<11) + +#define LXT2_WR_SYM_F_IN (1<<12) +#define LXT2_WR_SYM_F_OUT (1<<13) +#define LXT2_WR_SYM_F_INOUT (1<<14) + +#define LXT2_WR_SYM_F_WIRE (1<<15) +#define LXT2_WR_SYM_F_REG (1<<16) + + + /* file I/O */ +struct lxt2_wr_trace * lxt2_wr_init(const char *name); +void lxt2_wr_flush(struct lxt2_wr_trace *lt); +void lxt2_wr_close(struct lxt2_wr_trace *lt); + + /* for dealing with very large traces, split into multiple files approximately "siz" in length */ +void lxt2_wr_set_break_size(struct lxt2_wr_trace *lt, off_t siz); + + /* 0 = no compression, 9 = best compression, 4 = default */ +void lxt2_wr_set_compression_depth(struct lxt2_wr_trace *lt, unsigned int depth); + + /* default is partial off, turning on makes for faster trace reads, nonzero zipmode causes vertical compression */ +void lxt2_wr_set_partial_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_partial_on(struct lxt2_wr_trace *lt, int zipmode); +void lxt2_wr_set_partial_preference(struct lxt2_wr_trace *lt, const char *name); + + /* turning off checkpointing makes for smaller files */ +void lxt2_wr_set_checkpoint_off(struct lxt2_wr_trace *lt); +void lxt2_wr_set_checkpoint_on(struct lxt2_wr_trace *lt); + + /* facility creation */ +void lxt2_wr_set_initial_value(struct lxt2_wr_trace *lt, char value); +struct lxt2_wr_symbol * lxt2_wr_symbol_find(struct lxt2_wr_trace *lt, const char *name); +struct lxt2_wr_symbol * lxt2_wr_symbol_add(struct lxt2_wr_trace *lt, const char *name, unsigned int rows, int msb, int lsb, int flags); +struct lxt2_wr_symbol * lxt2_wr_symbol_alias(struct lxt2_wr_trace *lt, const char *existing_name, const char *alias, int msb, int lsb); +void lxt2_wr_symbol_bracket_stripping(struct lxt2_wr_trace *lt, int doit); + + /* each granule is LXT2_WR_GRANULE_SIZE (32 or 64) timesteps, default is 256 per section */ +void lxt2_wr_set_maxgranule(struct lxt2_wr_trace *lt, unsigned int maxgranule); + + /* time ops */ +void lxt2_wr_set_timescale(struct lxt2_wr_trace *lt, int timescale); +int lxt2_wr_set_time(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_inc_time_by_delta(struct lxt2_wr_trace *lt, unsigned int timeval); +int lxt2_wr_set_time64(struct lxt2_wr_trace *lt, lxttime_t timeval); +int lxt2_wr_inc_time_by_delta64(struct lxt2_wr_trace *lt, lxttime_t timeval); + + /* allows blackout regions in LXT files */ +void lxt2_wr_set_dumpoff(struct lxt2_wr_trace *lt); +void lxt2_wr_set_dumpon(struct lxt2_wr_trace *lt); + + /* left fill on bit_string uses vcd semantics (left fill with value[0] unless value[0]=='1', then use '0') */ +int lxt2_wr_emit_value_int(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, int value); +int lxt2_wr_emit_value_double(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, double value); +int lxt2_wr_emit_value_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); +int lxt2_wr_emit_value_bit_string(struct lxt2_wr_trace *lt, struct lxt2_wr_symbol *s, unsigned int row, char *value); + +#endif + + +#endif diff --git a/src/xspice/evt/evtlxt2.c b/src/xspice/evt/evtlxt2.c new file mode 100644 index 000000000..cb98c5dec --- /dev/null +++ b/src/xspice/evt/evtlxt2.c @@ -0,0 +1,128 @@ +#include "ngspice/ngspice.h" +#include "ngspice/cktdefs.h" +//#include "util.h" + +#include "ngspice/mif.h" +#include "ngspice/evt.h" +#include "ngspice/evtudn.h" + +#include "ngspice/mifproto.h" +#include "ngspice/evtproto.h" +#include "ngspice/cm.h" +#include + +static char *EVTbitmap(int i); + +/* +EVTemitlxt2 + + +This function emits a evt value into an lxt2 change-on-event file stream. (holmes) + +*/ + +void EVTemitlxt2( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The node to copy */ + Evt_Node_t *from) /* Location to copy from */ +{ + static unsigned int last_set_time; + static double time_resolution=-1.0; + unsigned int set_time; + int type; + char *name; + /* Mif_Boolean_t invert; */ + + Evt_Node_Info_t **node_table; + struct lxt2_wr_symbol **trace_table; + struct lxt2_wr_symbol *trace; + + node_table = ckt->evt->info.node_table; + trace_table = ckt->lxt2.evt_table; + + type = node_table[node_index]->udn_index; + name = node_table[node_index]->name; + + if(time_resolution<0.0) { + time_resolution=(double)pow(10.0,(double)LXT2_TIME_RESOLUTION_EXPONENT); + last_set_time=0; + #ifdef LXT2_DEBUG + printf("LXT2 time_resolution (%g) last_time(%d)\n", time_resolution,last_set_time); + #endif + } + set_time = (unsigned int)(ckt->CKTtime/time_resolution); + if(set_time > last_set_time) { + #ifdef LXT2_DEBUG + printf("LXT2 set time (%g) (%d)\n", ckt->CKTtime,set_time); + #endif + lxt2_wr_set_time(ckt->lxt2.file, set_time); + last_set_time = set_time; + } + + trace=(struct lxt2_wr_symbol *)(trace_table[node_index]); + + switch(type) + { + case 0: /* Bit */ + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c:trace(%p) emit bit (%s)->(%s)\n",trace,name,(char *)EVTbitmap(*((int *)(from->node_value)))); + #endif + if(lxt2_wr_emit_value_bit_string(ckt->lxt2.file,trace,0,(char *)EVTbitmap(*((int *)(from->node_value))))==0) { + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c:trace(%p) emit bit (%s)->(%s) SUCCESS\n",&trace,name,(char *)EVTbitmap(*((int *)(from->node_value)))); + #endif + }; + break; + case 1: /* Integer */ + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c:trace(%p) emit int (%s)->(%d)\n",trace,name,*((int *)(from->node_value))); + #endif + lxt2_wr_emit_value_int(ckt->lxt2.file,trace,0,*((int *)(from->node_value))); + break; + case 2: /* Double */ + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c:trace(%p) emit double (%s)->(%g)\n",trace,name,*((double *)(from->node_value))); + #endif + lxt2_wr_emit_value_double(ckt->lxt2.file,trace,0,*((double *)(from->node_value))); + break; + default: /* Bit */ + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c:trace(%p) emit bit (%s)->(%s)\n",trace,name,(char *)EVTbitmap(*((int *)(from->node_value)))); + #endif + lxt2_wr_emit_value_bit_string(ckt->lxt2.file,trace,0,(char *)EVTbitmap(*((int *)(from->node_value)))); + break; + } + #ifdef LXT2_DEBUG + printf("LXT2 evtlxt2.c: flush ckt.tracelxt2\n"); + #endif + lxt2_wr_flush(ckt->lxt2.file); + +} + +static char *EVTbitmap(int i) +{ + static char s[2]; + char *p = s; + + switch(i) + { + case 0: + *(p++) = '0'; + break; + case 1: + *(p++) = '1'; + break; + case 2: + *(p++) = 'u'; + break; + case 3: + *(p++) = 'z'; + break; + default: + *(p++) = 'u'; + break; + } + + *(p) = 0; + return(s); +}