From 06fdf7be587649ce6d2edfc16fdacaf6daba3361 Mon Sep 17 00:00:00 2001 From: Yves Mathieu <75626303+mathieu-tp@users.noreply.github.com> Date: Wed, 23 Nov 2022 10:07:14 +0100 Subject: [PATCH] Add support of Events for VCD/FST traces (#3759) --- include/verilated_fst_c.cpp | 10 +++ include/verilated_fst_c.h | 3 + include/verilated_trace.h | 11 ++++ include/verilated_trace_imp.h | 11 ++++ include/verilated_vcd_c.cpp | 16 +++++ include/verilated_vcd_c.h | 2 + src/V3EmitCImp.cpp | 7 ++- test_regress/t/t_timing_trace.out | 62 ++++++++++--------- test_regress/t/t_timing_trace.v | 8 ++- test_regress/t/t_timing_trace_fst.out | 87 +++++++++++++++++++++++++++ test_regress/t/t_timing_trace_fst.pl | 32 ++++++++++ 11 files changed, 218 insertions(+), 31 deletions(-) create mode 100644 test_regress/t/t_timing_trace_fst.out create mode 100755 test_regress/t/t_timing_trace_fst.pl diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 6e1f5a33b..de5f4bbc4 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -221,6 +221,10 @@ void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, fstVar } } +void VerilatedFst::declEvent(uint32_t code, const char* name, int dtypenum, fstVarDir vardir, + fstVarType vartype, bool array, int arraynum) { + declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0); +} void VerilatedFst::declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir, fstVarType vartype, bool array, int arraynum) { declare(code, name, dtypenum, vardir, vartype, array, arraynum, false, 0, 0); @@ -279,6 +283,12 @@ void VerilatedFst::configure(const VerilatedTraceConfig& config) { // verilated_trace_imp.h, which is included in this file at the top), // so always inline them. +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitEvent(uint32_t code, VlEvent newval) { + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + fstWriterEmitValueChange(m_fst, m_symbolp[code], "1"); +} + VL_ATTR_ALWINLINE void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) { VL_DEBUG_IFDEF(assert(m_symbolp[code]);); diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index fafcd415c..9e92f12bf 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -101,6 +101,8 @@ public: //========================================================================= // Internal interface to Verilator generated code + void declEvent(uint32_t code, const char* name, int dtypenum, fstVarDir vardir, + fstVarType vartype, bool array, int arraynum); void declBit(uint32_t code, const char* name, int dtypenum, fstVarDir vardir, fstVarType vartype, bool array, int arraynum); void declBus(uint32_t code, const char* name, int dtypenum, fstVarDir vardir, @@ -161,6 +163,7 @@ class VerilatedFstBuffer VL_NOT_FINAL { // Implementations of duck-typed methods for VerilatedTraceBuffer. These are // called from only one place (the full* methods), so always inline them. + VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval); VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 2d75ea0ab..2a19c6795 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -107,6 +107,7 @@ public: CHG_QDATA = 0x5, CHG_WDATA = 0x6, CHG_DOUBLE = 0x8, + CHG_EVENT = 0x9, // TODO: full.. TIME_CHANGE = 0xc, TRACE_BUFFER = 0xd, @@ -422,6 +423,7 @@ public: void fullQData(uint32_t* oldp, QData newval, int bits); void fullWData(uint32_t* oldp, const WData* newvalp, int bits); void fullDouble(uint32_t* oldp, double newval); + void fullEvent(uint32_t* oldp, VlEvent newval); // In non-offload mode, these are called directly by the trace callbacks, // and are called chg*. In offload mode, they are called by the worker @@ -456,6 +458,9 @@ public: } } } + VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, VlEvent newval) { + fullEvent(oldp, newval); + } VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) { // cppcheck-suppress invalidPointerCast if (VL_UNLIKELY(*reinterpret_cast(oldp) != newval)) fullDouble(oldp, newval); @@ -533,6 +538,12 @@ public: m_offloadBufferWritep += 4; VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp);); } + void chgEvent(uint32_t code, VlEvent newval) { + m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT ; + m_offloadBufferWritep[1] = code; + m_offloadBufferWritep += 2; + VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp);); + } }; #endif // guard diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index c863649c6..aa37ff9fb 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -183,6 +183,10 @@ void VerilatedTrace::offloadWorkerThreadMain() { traceBufp->chgDouble(oldp, *reinterpret_cast(readp)); readp += 2; continue; + case VerilatedTraceOffloadCommand::CHG_EVENT: + VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top); + traceBufp->chgEvent(oldp, *reinterpret_cast(readp)); + continue; //=== // Rare commands @@ -834,6 +838,13 @@ void VerilatedTraceBuffer::fullBit(uint32_t* oldp, CData newval) { emitBit(code, newval); } +template <> +void VerilatedTraceBuffer::fullEvent(uint32_t* oldp, VlEvent newval) { + const uint32_t code = oldp - m_sigs_oldvalp; + *oldp = 1; // Do we really store an "event" ? + emitEvent(code, newval); +} + template <> void VerilatedTraceBuffer::fullCData(uint32_t* oldp, CData newval, int bits) { const uint32_t code = oldp - m_sigs_oldvalp; diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index a0145b2af..c313e044c 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -548,6 +548,9 @@ void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, b m_namemapp->emplace(hiername, decl); } +void VerilatedVcd::declEvent(uint32_t code, const char* name, bool array, int arraynum) { + declare(code, name, "event", array, arraynum, false, false, 0, 0); +} void VerilatedVcd::declBit(uint32_t code, const char* name, bool array, int arraynum) { declare(code, name, "wire", array, arraynum, false, false, 0, 0); } @@ -694,6 +697,19 @@ void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) { // verilated_trace_imp.h, which is included in this file at the top), // so always inline them. +VL_ATTR_ALWINLINE +void VerilatedVcdBuffer::emitEvent(uint32_t code, VlEvent newval) { + const bool triggered = newval.isTriggered(); + // TODO : It seems that untriggerd events are not filtered + // should be tested before this last step + if(triggered) { + // Don't prefetch suffix as it's a bit too late; + char* wp = m_writep; + *wp++ = '1' ; + finishLine(code, wp); + } +} + VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) { // Don't prefetch suffix as it's a bit too late; diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index 166a48322..64469d430 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -140,6 +140,7 @@ public: //========================================================================= // Internal interface to Verilator generated code + void declEvent(uint32_t code, const char* name, bool array, int arraynum); void declBit(uint32_t code, const char* name, bool array, int arraynum); void declBus(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb); void declQuad(uint32_t code, const char* name, bool array, int arraynum, int msb, int lsb); @@ -206,6 +207,7 @@ class VerilatedVcdBuffer VL_NOT_FINAL { // Implementation of VerilatedTraceBuffer interface // Implementations of duck-typed methods for VerilatedTraceBuffer. These are // called from only one place (the full* methods), so always inline them. + VL_ATTR_ALWINLINE void emitEvent(uint32_t code, VlEvent newval); VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index cf6fa6ccd..b7db6e5b2 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -637,6 +637,8 @@ class EmitCTrace final : EmitCFunc { puts("tracep->declQuad"); } else if (nodep->bitRange().ranged()) { puts("tracep->declBus"); + } else if (nodep->dtypep()->basicp()->isEvent()) { + puts("tracep->declEvent"); } else { puts("tracep->declBit"); } @@ -689,11 +691,11 @@ class EmitCTrace final : EmitCFunc { else if (kwd == VBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; } else if (kwd == VBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; } else if (kwd == VBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; } + else if (kwd == VBasicDTypeKwd::EVENT) { fstvt = "FST_VT_VCD_EVENT"; } else { fstvt = "FST_VT_SV_BIT"; } // clang-format on // // Not currently supported - // FST_VT_VCD_EVENT // FST_VT_VCD_PORT // FST_VT_VCD_SHORTREAL // FST_VT_VCD_REALTIME @@ -779,6 +781,9 @@ class EmitCTrace final : EmitCFunc { puts("bufp->" + func + "SData"); } else if (nodep->declp()->widthMin() > 1) { puts("bufp->" + func + "CData"); + } else if (nodep->dtypep()->basicp()->isEvent()) { + puts("bufp->" + func + "Event"); + emitWidth = false; } else { puts("bufp->" + func + "Bit"); emitWidth = false; diff --git a/test_regress/t/t_timing_trace.out b/test_regress/t/t_timing_trace.out index f4c2f96b8..f1a84fcb5 100644 --- a/test_regress/t/t_timing_trace.out +++ b/test_regress/t/t_timing_trace.out @@ -1,16 +1,17 @@ $version Generated by VerilatedVcd $end -$date Thu Oct 20 09:56:59 2022 $end +$date Tue Nov 22 16:48:14 2022 $end $timescale 1ps $end $scope module TOP $end $scope module t $end - $var wire 32 * CLK_HALF_PERIOD [31:0] $end - $var wire 32 ) CLK_PERIOD [31:0] $end + $var wire 32 + CLK_HALF_PERIOD [31:0] $end + $var wire 32 * CLK_PERIOD [31:0] $end $var wire 1 $ a $end $var wire 1 % b $end $var wire 1 & c $end - $var wire 1 ( clk $end + $var wire 1 ) clk $end $var wire 1 ' d $end + $var event 1 ( ev $end $var wire 1 # rst $end $upscope $end $upscope $end @@ -23,54 +24,57 @@ $enddefinitions $end 0% 0& 0' -0( -b00000000000000000000000000001010 ) -b00000000000000000000000000000101 * +0) +b00000000000000000000000000001010 * +b00000000000000000000000000000101 + #5 -1( +1) #10 0# 1% -0( +1( +0) #15 -1( +1) #20 -0( +1( +0) #25 -1( +1) #30 -0( +0) #35 -1( +1) #40 -0( +0) #45 -1( +1) #50 -0( +0) #55 -1( +1) #60 -0( +0) #65 -1( +1) #70 -0( +0) #75 -1( +1) #80 -0( +0) #85 -1( +1) #90 -0( +0) #95 -1( +1) #100 -0( +0) #105 -1( +1) #110 1# 0% -0( +1( +0) diff --git a/test_regress/t/t_timing_trace.v b/test_regress/t/t_timing_trace.v index af789dbeb..9893856cc 100644 --- a/test_regress/t/t_timing_trace.v +++ b/test_regress/t/t_timing_trace.v @@ -16,6 +16,7 @@ module t; logic b; logic c; logic d; + event ev; initial begin $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); @@ -34,10 +35,15 @@ module t; #CLK_PERIOD; rst = 0; b = 1; + -> ev ; + #CLK_PERIOD; + -> ev ; - #(10 * CLK_PERIOD); + #(9 * CLK_PERIOD); + -> ev ; $write("*-* All Finished *-*\n"); $finish; end + endmodule diff --git a/test_regress/t/t_timing_trace_fst.out b/test_regress/t/t_timing_trace_fst.out new file mode 100644 index 000000000..a0170939c --- /dev/null +++ b/test_regress/t/t_timing_trace_fst.out @@ -0,0 +1,87 @@ +$date + Tue Nov 22 18:14:18 2022 + +$end +$version + fstWriter +$end +$timescale + 1ps +$end +$scope module TOP $end +$scope module t $end +$var parameter 32 ! CLK_PERIOD [31:0] $end +$var parameter 32 " CLK_HALF_PERIOD [31:0] $end +$var logic 1 # rst $end +$var logic 1 $ clk $end +$var logic 1 % a $end +$var logic 1 & b $end +$var logic 1 ' c $end +$var logic 1 ( d $end +$var event 1 ) ev $end +$upscope $end +$upscope $end +$enddefinitions $end +#0 +$dumpvars +1) +0( +0' +0& +0% +0$ +1# +b00000000000000000000000000000101 " +b00000000000000000000000000001010 ! +$end +#5 +1$ +#10 +0$ +0# +1& +1) +#15 +1$ +#20 +0$ +1) +#25 +1$ +#30 +0$ +#35 +1$ +#40 +0$ +#45 +1$ +#50 +0$ +#55 +1$ +#60 +0$ +#65 +1$ +#70 +0$ +#75 +1$ +#80 +0$ +#85 +1$ +#90 +0$ +#95 +1$ +#100 +0$ +#105 +1$ +#110 +0$ +1) +0& +1# diff --git a/test_regress/t/t_timing_trace_fst.pl b/test_regress/t/t_timing_trace_fst.pl new file mode 100755 index 000000000..0ac004740 --- /dev/null +++ b/test_regress/t/t_timing_trace_fst.pl @@ -0,0 +1,32 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 Yves Mathieu. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_timing_trace.v"); + +if (!$Self->have_coroutines) { + skip("No coroutine support"); +} +else { + compile( + verilator_flags2 => ["--exe --main --timing --trace-fst -Wno-MINTYPMAXDLY"], + make_main => 0, + ); + + execute( + check_finished => 1, + ); + + fst_identical($Self->trace_filename, $Self->{golden_filename}); +} + +ok(1); +1;