diff --git a/Changes b/Changes index 36e6c25a8..cbd7f90bc 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,11 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.005 devel +*** For --trace-fst, save enum decoding information, bug1358. [Sergi Granell] + (To visualize enumeration data you must use GTKwave 3.3.95 or newer.) + +*** For --trace-fst, instead of *.fst.hier, put data into *.fst. [Tony Bybell] + * Verilator 4.004 2018-10-6 diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index 132a92417..016511844 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -795,6 +795,8 @@ char *geom_handle_nam; char *valpos_handle_nam; char *curval_handle_nam; char *tchn_handle_nam; + +fstEnumHandle max_enumhandle; }; @@ -2305,7 +2307,7 @@ if(xc && path && path[0]) const unsigned char *path2 = (const unsigned char *)path; PPvoid_t pv; #else - char *path2 = alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ + char *path2 = (char *)alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ PPvoid_t pv; strcpy(path2, path); #endif @@ -2726,6 +2728,111 @@ if(xc) } +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, const char **literal_arr, const char **val_arr) +{ +fstEnumHandle handle = 0; +unsigned int *literal_lens = NULL; +unsigned int *val_lens = NULL; +int lit_len_tot = 0; +int val_len_tot = 0; +int name_len; +char elem_count_buf[16]; +int elem_count_len; +int total_len; +int pos = 0; +char *attr_str = NULL; + +if(ctx && name && literal_arr && val_arr && (elem_count != 0)) + { + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + uint32_t i; + + name_len = strlen(name); + elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count); + + literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + + for(i=0;i 0) + { + if(val_lens[i] < min_valbits) + { + val_len_tot += (min_valbits - val_lens[i]); /* additional converted len is same for '0' character */ + } + } + } + + total_len = name_len + 1 + elem_count_len + 1 + lit_len_tot + elem_count + val_len_tot + elem_count; + + attr_str = (char*)malloc(total_len); + pos = 0; + + memcpy(attr_str+pos, name, name_len); + pos += name_len; + attr_str[pos++] = ' '; + + memcpy(attr_str+pos, elem_count_buf, elem_count_len); + pos += elem_count_len; + attr_str[pos++] = ' '; + + for(i=0;i 0) + { + if(val_lens[i] < min_valbits) + { + memset(attr_str+pos, '0', min_valbits - val_lens[i]); + pos += (min_valbits - val_lens[i]); + } + } + + pos += fstUtilityBinToEsc((unsigned char*)attr_str+pos, (unsigned char*)val_arr[i], val_lens[i]); + attr_str[pos++] = ' '; + } + + attr_str[pos-1] = 0; + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS"fstWriterCreateEnumTable() total_len: %d, pos: %d\n", total_len, pos); + fprintf(stderr, FST_APIMESS"*%s*\n", attr_str); +#endif + + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, attr_str, handle = ++xc->max_enumhandle); + + free(attr_str); + free(val_lens); + free(literal_lens); + } + +return(handle); +} + + +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle) +{ +struct fstWriterContext *xc = (struct fstWriterContext *)ctx; +if(xc && handle) + { + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, NULL, handle); + } +} + + /* * value and time change emission */ @@ -6503,9 +6610,46 @@ if(base && *base) /*** ***/ /************************/ -int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len) +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len) { -unsigned char *src = s; +const unsigned char *src = s; +int dlen = 0; +int i; + +for(i=0;i ' ') && (src[i] <= '~')) /* no white spaces in output */ + { + dlen++; + } + else + { + dlen += 4; + } + break; + } + } + +return(dlen); +} + + +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len) +{ +const unsigned char *src = s; unsigned char *dst = d; unsigned char val; int i; @@ -6604,3 +6748,76 @@ for(i=0;ielem_count = cnt; + et->name = strdup(s); + et->literal_arr = (char**)calloc(cnt, sizeof(char *)); + et->val_arr = (char**)calloc(cnt, sizeof(char *)); + + sp = strchr(et->name, ' '); + *sp = 0; + + sp = strchr(sp+1, ' '); + + for(i=0;iliteral_arr[i] = sp+1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char*)et->literal_arr[i], strlen(et->literal_arr[i])); + et->literal_arr[i][newlen] = 0; + } + + for(i=0;ival_arr[i] = sp+1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char*)et->val_arr[i], strlen(et->val_arr[i])); + et->val_arr[i][newlen] = 0; + } + } + } + +return(et); +} + + +void fstUtilityFreeEnumTable(struct fstETab *etab) +{ +if(etab) + { + free(etab->literal_arr); + free(etab->val_arr); + free(etab->name); + free(etab); + } +} diff --git a/include/gtkwave/fstapi.h b/include/gtkwave/fstapi.h index e66c20467..aef6de23b 100644 --- a/include/gtkwave/fstapi.h +++ b/include/gtkwave/fstapi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2017 Tony Bybell. + * Copyright (c) 2009-2018 Tony Bybell. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -39,6 +39,7 @@ extern "C" { #define FST_RDLOAD "FSTLOAD | " typedef uint32_t fstHandle; +typedef uint32_t fstEnumHandle; enum fstWriterPackType { FST_WR_PT_ZLIB = 0, @@ -196,9 +197,10 @@ enum fstMiscType { FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */ FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */ FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */ - FST_MT_UNKNOWN = 7, + FST_MT_ENUMTABLE = 7, /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */ + FST_MT_UNKNOWN = 8, - FST_MT_MAX = 7 + FST_MT_MAX = 8 }; enum fstArrayType { @@ -228,7 +230,10 @@ enum fstEnumValueType { FST_EV_SV_UNSIGNED_LONGINT = 12, FST_EV_SV_UNSIGNED_BYTE = 13, - FST_EV_MAX = 13 + FST_EV_REG = 14, + FST_EV_TIME = 15, + + FST_EV_MAX = 15 }; enum fstPackType { @@ -324,11 +329,21 @@ union { }; +struct fstETab +{ +char *name; +uint32_t elem_count; +char **literal_arr; +char **val_arr; +}; + + /* * writer functions */ void fstWriterClose(void *ctx); void * fstWriterCreate(const char *nam, int use_compressed_hier); +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, const char **literal_arr, const char **val_arr); /* used for Verilog/SV */ fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle); @@ -337,9 +352,10 @@ fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, enum fstSupplementalDataType sdt); +void fstWriterEmitDumpActive(void *ctx, int enable); +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle); void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val); void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len); -void fstWriterEmitDumpActive(void *ctx, int enable); void fstWriterEmitTimeChange(void *ctx, uint64_t tim); void fstWriterFlushContext(void *ctx); int fstWriterGetDumpSizeLimitReached(void *ctx); @@ -422,8 +438,12 @@ void fstReaderSetVcdExtensions(void *ctx, int enable); /* * utility functions */ -int fstUtilityBinToEsc(unsigned char *d, unsigned char *s, int len); +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */ +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len); int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len); +struct fstETab *fstUtilityExtractEnumTableFromString(const char *s); +void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */ + #ifdef __cplusplus } diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 7520a6ee5..6e307ce17 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -94,8 +94,16 @@ void VerilatedFst::module(const std::string& name) { //============================================================================= // Decl -void VerilatedFst::declSymbol(vluint32_t code, const char* name, fstVarDir vardir, - fstVarType vartype, +void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, vluint32_t elements, + unsigned int minValbits, + const char** itemNamesp, const char** itemValuesp) { + fstEnumHandle enumNum = fstWriterCreateEnumTable(m_fst, name, elements, + minValbits, itemNamesp, itemValuesp); + m_local2fstdtype[dtypenum] = enumNum; +} + +void VerilatedFst::declSymbol(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum, vluint32_t len) { std::pair p = m_code2symbol.insert(std::make_pair(code, (fstHandle)(0))); @@ -135,6 +143,10 @@ void VerilatedFst::declSymbol(vluint32_t code, const char* name, fstVarDir vardi name_ss << "(" << arraynum << ")"; std::string name_str = name_ss.str(); + if (dtypenum > 0) { + fstEnumHandle enumNum = m_local2fstdtype[dtypenum]; + fstWriterEmitEnumTableRef(m_fst, enumNum); + } if (p.second) { // New p.first->second = fstWriterCreateVar(m_fst, vartype, vardir, len, name_str.c_str(), 0); assert(p.first->second); diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index 2c99ffbd1..0bd2d232b 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -43,6 +43,7 @@ typedef void (*VerilatedFstCallback_t)(VerilatedFst* vcdp, void* userthis, vluin class VerilatedFst { typedef std::map Code2SymbolType; + typedef std::map Local2FstDtype; typedef std::vector CallbackVec; private: void* m_fst; @@ -52,11 +53,12 @@ private: std::string m_module; CallbackVec m_callbacks; ///< Routines to perform dumping Code2SymbolType m_code2symbol; + Local2FstDtype m_local2fstdtype; std::list m_curScope; // CONSTRUCTORS VL_UNCOPYABLE(VerilatedFst); void declSymbol(vluint32_t code, const char* name, - fstVarDir vardir, fstVarType vartype, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum, vluint32_t len); // helpers std::vector m_valueStrBuffer; @@ -96,30 +98,40 @@ public: /// Inside dumping routines, declare a module void module(const std::string& name); + /// Inside dumping routines, declare a data type + void declDTypeEnum(int dtypenum, const char* name, vluint32_t elements, + unsigned int minValbits, + const char** itemNamesp, const char** itemValuesp); /// Inside dumping routines, declare a signal - void declBit(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declBit(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum) { - declSymbol(code, name, vardir, vartype, arraynum, 1); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 1); } - void declBus(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declBus(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum, int msb, int lsb) { - declSymbol(code, name, vardir, vartype, arraynum, msb - lsb + 1); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1); } - void declDouble(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declDouble(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum) { - declSymbol(code, name, vardir, vartype, arraynum, 2); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 2); } - void declFloat(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declFloat(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum) { - declSymbol(code, name, vardir, vartype, arraynum, 1); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, 1); } - void declQuad(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declQuad(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum, int msb, int lsb) { - declSymbol(code, name, vardir, vartype, arraynum, msb - lsb + 1); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1); } - void declArray(vluint32_t code, const char* name, fstVarDir vardir, fstVarType vartype, + void declArray(vluint32_t code, const char* name, + int dtypenum, fstVarDir vardir, fstVarType vartype, int arraynum, int msb, int lsb) { - declSymbol(code, name, vardir, vartype, arraynum, msb - lsb + 1); + declSymbol(code, name, dtypenum, vardir, vartype, arraynum, msb - lsb + 1); } /// Inside dumping routines, dump one signal if it has changed diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index f40b65689..d95aded86 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -2715,8 +2715,15 @@ void EmitCImp::main(AstNodeModule* modp, bool slow, bool fast) { // Tracing routines class EmitCTrace : EmitCStmts { + // NODE STATE/TYPES + // Cleared on netlist + // AstNode::user1() -> int. Enum number + AstUser1InUse m_inuser1; + + // MEMBERS AstCFunc* m_funcp; // Function we're in now bool m_slow; // Making slow file + int m_enumNum; // Enumeration number (whole netlist) // METHODS void newOutCFile(int filenum) { @@ -2819,7 +2826,7 @@ class EmitCTrace : EmitCStmts { return varp->isSc() && varp->isScUint(); } - void emitTraceInitOne(AstTraceDecl* nodep) { + void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { if (nodep->dtypep()->basicp()->isDouble()) { puts("vcdp->declDouble"); } else if (nodep->isWide()) { @@ -2838,6 +2845,7 @@ class EmitCTrace : EmitCStmts { putsQuoted(nodep->showname()); // Direction if (v3Global.opt.traceFormat() == TraceFormat::FST) { + puts(","+cvtToStr(enumNum)); // fstVarDir if (nodep->declInout()) puts(",FST_VD_INOUT"); else if (nodep->declInput()) puts(",FST_VD_INPUT"); @@ -2900,6 +2908,49 @@ class EmitCTrace : EmitCStmts { puts(");"); } + int emitTraceDeclDType(AstNodeDType* nodep) { + // Return enum number or -1 for none + if (v3Global.opt.traceFormat() == TraceFormat::FST) { + // Skip over refs-to-refs, but stop before final ref so can get data type name + // Alternatively back in V3Width we could have push enum names from upper typedefs + if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) { + int enumNum = enump->user1(); + if (!enumNum) { + enumNum = ++m_enumNum; + enump->user1(enumNum); + int nvals = 0; + puts("{\n"); + puts("const char* __VenumItemNames[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp=VN_CAST(itemp->nextp(), EnumItem)) { + if (++nvals > 1) puts(", "); + putbs("\""+itemp->prettyName()+"\""); + } + puts("};\n"); + nvals = 0; + puts("const char* __VenumItemValues[]\n"); + puts("= {"); + for (AstEnumItem* itemp = enump->itemsp(); itemp; + itemp=VN_CAST(itemp->nextp(), EnumItem)) { + AstConst* constp = VN_CAST(itemp->valuep(), Const); + if (++nvals > 1) puts(", "); + putbs("\""+constp->num().displayed(nodep->fileline(), "%0b")+"\""); + } + puts("};\n"); + puts("vcdp->declDTypeEnum("+cvtToStr(enumNum) + +", \""+enump->prettyName()+"\", " + +cvtToStr(nvals) + +", "+cvtToStr(enump->widthMin()) + +", __VenumItemNames, __VenumItemValues);\n"); + puts("}\n"); + } + return enumNum; + } + } + return -1; + } + void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { iterateAndNextNull(nodep->precondsp()); string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL @@ -3013,12 +3064,13 @@ class EmitCTrace : EmitCStmts { m_funcp = NULL; } virtual void visit(AstTraceDecl* nodep) { + int enumNum = emitTraceDeclDType(nodep->dtypep()); if (nodep->arrayRange().ranged()) { puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayRange().elements())+"; i++) {\n"); - emitTraceInitOne(nodep); + emitTraceInitOne(nodep, enumNum); puts("}}\n"); } else { - emitTraceInitOne(nodep); + emitTraceInitOne(nodep, enumNum); puts("\n"); } } @@ -3041,6 +3093,7 @@ public: explicit EmitCTrace(bool slow) { m_funcp = NULL; m_slow = slow; + m_enumNum = 0; } virtual ~EmitCTrace() {} void main() { diff --git a/test_regress/t/t_trace_complex_fst.out b/test_regress/t/t_trace_complex_fst.out index 29596294f..9c3f1f26c 100644 --- a/test_regress/t/t_trace_complex_fst.out +++ b/test_regress/t/t_trace_complex_fst.out @@ -1,5 +1,5 @@ $date - Sun Oct 7 21:45:40 2018 + Mon Oct 8 07:13:10 2018 $end $version @@ -33,7 +33,10 @@ $var real 64 3 v_real $end $var real 64 4 v_arr_real(0) $end $var real 64 5 v_arr_real(1) $end $var logic 64 6 v_str32x2 $end +$attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end +$attrbegin misc 07 "" 1 $end $var logic 32 7 v_enumed $end +$attrbegin misc 07 "" 1 $end $var logic 32 8 v_enumed2 $end $scope module unnamedblk1 $end $var integer 32 9 b $end diff --git a/test_regress/t/t_trace_complex_params_fst.out b/test_regress/t/t_trace_complex_params_fst.out index da3667e03..ace3ea234 100644 --- a/test_regress/t/t_trace_complex_params_fst.out +++ b/test_regress/t/t_trace_complex_params_fst.out @@ -1,5 +1,5 @@ $date - Sun Oct 7 21:45:42 2018 + Mon Oct 8 07:13:11 2018 $end $version @@ -33,7 +33,10 @@ $var real 64 3 v_real $end $var real 64 4 v_arr_real(0) $end $var real 64 5 v_arr_real(1) $end $var logic 64 6 v_str32x2 $end +$attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end +$attrbegin misc 07 "" 1 $end $var logic 32 7 v_enumed $end +$attrbegin misc 07 "" 1 $end $var logic 32 8 v_enumed2 $end $scope module unnamedblk1 $end $var integer 32 9 b $end diff --git a/test_regress/t/t_trace_complex_structs_fst.out b/test_regress/t/t_trace_complex_structs_fst.out index 2e7b495c1..0fe981324 100644 --- a/test_regress/t/t_trace_complex_structs_fst.out +++ b/test_regress/t/t_trace_complex_structs_fst.out @@ -1,5 +1,5 @@ $date - Sun Oct 7 21:45:44 2018 + Mon Oct 8 07:13:12 2018 $end $version @@ -72,8 +72,11 @@ $var logic 32 A data $end $upscope $end $scope module v_str32x2(1) $end $var logic 32 B data $end +$attrbegin misc 07 t.enumed_t 4 ZERO ONE TWO THREE 00000000000000000000000000000000 00000000000000000000000000000001 00000000000000000000000000000010 00000000000000000000000000000011 1 $end $upscope $end +$attrbegin misc 07 "" 1 $end $var logic 32 C v_enumed $end +$attrbegin misc 07 "" 1 $end $var logic 32 D v_enumed2 $end $scope module unnamedblk1 $end $var integer 32 E b $end