diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 6ebfece29..2cd744a85 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -31,6 +31,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { // MEMBERS bool m_suppressSemi = false; + bool m_suppressUnknown = false; AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE // METHODS @@ -51,12 +52,13 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } // VISITORS - virtual void visit(AstNetlist* nodep) override { iterateChildren(nodep); } + virtual void visit(AstNetlist* nodep) override { iterateAndNextNull(nodep->modulesp()); } virtual void visit(AstNodeModule* nodep) override { putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n"); iterateChildren(nodep); putqs(nodep, "end" + nodep->verilogKwd() + "\n"); } + virtual void visit(AstPort* nodep) override {} virtual void visit(AstNodeFTask* nodep) override { putfs(nodep, nodep->isFunction() ? "function" : "task"); puts(" "); @@ -212,14 +214,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) { putfs(nodep, nodep->verilogKwd()); - putbs(" ("); + putbs("("); if (fileOrStrgp) { iterateAndNextNull(fileOrStrgp); - putbs(","); + putbs(", "); } putsQuoted(text); for (AstNode* expp = exprsp; expp; expp = expp->nextp()) { - puts(","); + puts(", "); iterateAndNextNull(expp); } puts(");\n"); @@ -247,7 +249,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { putfs(nodep, nodep->verilogKwd()); putbs("("); iterateAndNextNull(nodep->filenamep()); - putbs(","); + putbs(", "); iterateAndNextNull(nodep->modep()); puts(");\n"); } @@ -259,13 +261,13 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } virtual void visit(AstFClose* nodep) override { putfs(nodep, nodep->verilogKwd()); - putbs(" ("); + putbs("("); if (nodep->filep()) iterateAndNextNull(nodep->filep()); puts(");\n"); } virtual void visit(AstFFlush* nodep) override { putfs(nodep, nodep->verilogKwd()); - putbs(" ("); + putbs("("); if (nodep->filep()) iterateAndNextNull(nodep->filep()); puts(");\n"); } @@ -282,16 +284,16 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } virtual void visit(AstNodeReadWriteMem* nodep) override { putfs(nodep, nodep->verilogKwd()); - putbs(" ("); + putbs("("); if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep()); - putbs(","); + putbs(", "); if (nodep->memp()) iterateAndNextNull(nodep->memp()); if (nodep->lsbp()) { - putbs(","); + putbs(", "); iterateAndNextNull(nodep->lsbp()); } if (nodep->msbp()) { - putbs(","); + putbs(", "); iterateAndNextNull(nodep->msbp()); } puts(");\n"); @@ -302,7 +304,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } virtual void visit(AstSysIgnore* nodep) override { putfs(nodep, nodep->verilogKwd()); - putbs(" ("); + putbs("("); iterateAndNextNull(nodep->exprsp()); puts(");\n"); } @@ -358,7 +360,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { putfs(nodep, "$past("); iterateAndNextNull(nodep->exprp()); if (nodep->ticksp()) { - puts(","); + puts(", "); iterateAndNextNull(nodep->ticksp()); } puts(")"); @@ -482,7 +484,7 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { putfs(nodep, "$_ATTROF("); iterateAndNextNull(nodep->fromp()); if (nodep->dimp()) { - putbs(","); + putbs(", "); iterateAndNextNull(nodep->dimp()); } puts(")"); @@ -654,13 +656,17 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { puts(string("\n???? // ") + nodep->prettyTypeName() + "\n"); iterateChildren(nodep); // Not v3fatalSrc so we keep processing - nodep->v3error("Internal: Unknown node type reached emitter: " << nodep->prettyTypeName()); + if (!m_suppressUnknown) { + nodep->v3error( + "Internal: Unknown node type reached emitter: " << nodep->prettyTypeName()); + } } public: bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars - explicit EmitVBaseVisitor(AstSenTree* domainp = nullptr) - : m_sensesp{domainp} {} + explicit EmitVBaseVisitor(bool suppressUnknown, AstSenTree* domainp) + : m_suppressUnknown{suppressUnknown} + , m_sensesp{domainp} {} virtual ~EmitVBaseVisitor() override {} }; @@ -679,8 +685,9 @@ class EmitVFileVisitor : public EmitVBaseVisitor { virtual void putsNoTracking(const string& str) override { ofp()->putsNoTracking(str); } public: - EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText = false, - bool suppressVarSemi = false) { + EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp, bool trackText, bool suppressVarSemi, + bool suppressUnknown) + : EmitVBaseVisitor{suppressUnknown, nullptr} { m_ofp = ofp; m_trackText = trackText; m_suppressVarSemi = suppressVarSemi; @@ -704,7 +711,8 @@ class EmitVStreamVisitor : public EmitVBaseVisitor { public: EmitVStreamVisitor(AstNode* nodep, std::ostream& os) - : m_os(os) { // Need () or GCC 4.8 false warning + : EmitVBaseVisitor{false, nullptr} + , m_os(os) { // Need () or GCC 4.8 false warning iterate(nodep); } virtual ~EmitVStreamVisitor() override {} @@ -747,9 +755,8 @@ public: , m_prefix{prefix} , m_flWidth{flWidth} { m_column = 0; - m_prefixFl - = v3Global.rootp() - ->fileline(); // NETLIST's fileline instead of nullptr to avoid nullptr checks + m_prefixFl = v3Global.rootp()->fileline(); // NETLIST's fileline instead of nullptr to + // avoid nullptr checks } virtual ~EmitVPrefixedFormatter() override { if (m_column) puts("\n"); @@ -780,7 +787,7 @@ class EmitVPrefixedVisitor : public EmitVBaseVisitor { public: EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark) - : EmitVBaseVisitor{domainp} + : EmitVBaseVisitor{false, domainp} , m_formatter{os, prefix, flWidth} { if (user3mark) { AstUser3InUse::check(); } iterate(nodep); @@ -807,7 +814,14 @@ void V3EmitV::emitvFiles() { V3OutVFile of(vfilep->name()); of.puts("// DESCR" "IPTION: Verilator generated Verilog\n"); - EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, true); + EmitVFileVisitor visitor(vfilep->tblockp(), &of, true, true, false); } } } + +void V3EmitV::debugEmitV(const string& stage) { + UINFO(2, __FUNCTION__ << ": " << endl); + string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__" + stage + ".v"; + V3OutVFile of(filename); + EmitVFileVisitor visitor(v3Global.rootp(), &of, true, false, true); +} diff --git a/src/V3EmitV.h b/src/V3EmitV.h index 5ccf5c9b8..4a4900895 100644 --- a/src/V3EmitV.h +++ b/src/V3EmitV.h @@ -31,6 +31,7 @@ public: static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark); static void emitvFiles(); + static void debugEmitV(const string& stage); }; #endif // Guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 17a63b000..e8ffdb6f5 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1013,6 +1013,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char m_debugCheck = flag; } else if (onoff(sw, "-debug-collision", flag /*ref*/)) { // Undocumented m_debugCollision = flag; + } else if (onoff(sw, "-debug-emitv", flag /*ref*/)) { // Undocumented + m_debugEmitV = flag; } else if (onoff(sw, "-debug-exit-parse", flag /*ref*/)) { // Undocumented m_debugExitParse = flag; } else if (onoff(sw, "-debug-exit-uvm", flag /*ref*/)) { // Undocumented diff --git a/src/V3Options.h b/src/V3Options.h index d8a0f17b7..9ea261521 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -265,6 +265,7 @@ private: bool m_coverageUser = false; // main switch: --coverage-func bool m_debugCheck = false; // main switch: --debug-check bool m_debugCollision = false; // main switch: --debug-collision + bool m_debugEmitV = false; // main switch: --debug-emitv bool m_debugExitParse = false; // main switch: --debug-exit-parse bool m_debugExitUvm = false; // main switch: --debug-exit-uvm bool m_debugLeak = true; // main switch: --debug-leak @@ -469,6 +470,7 @@ public: bool coverageUser() const { return m_coverageUser; } bool debugCheck() const { return m_debugCheck; } bool debugCollision() const { return m_debugCollision; } + bool debugEmitV() const { return m_debugEmitV; } bool debugExitParse() const { return m_debugExitParse; } bool debugExitUvm() const { return m_debugExitUvm; } bool debugLeak() const { return m_debugLeak; } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 7ffce2b63..147199e81 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -106,6 +106,7 @@ static void reportStatsIfEnabled() { V3Stats::statsFinalAll(v3Global.rootp()); V3Stats::statsReport(); } + if (v3Global.opt.debugEmitV()) V3EmitV::debugEmitV("final"); } static void process() { @@ -363,6 +364,7 @@ static void process() { V3ActiveTop::activeTopAll(v3Global.rootp()); if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder"); + if (v3Global.opt.debugEmitV()) V3EmitV::debugEmitV("preorder"); // Order the code; form SBLOCKs and BLOCKCALLs V3Order::orderAll(v3Global.rootp()); diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out new file mode 100644 index 000000000..1170727af --- /dev/null +++ b/test_regress/t/t_debug_emitv.out @@ -0,0 +1,196 @@ +module Vt_debug_emitv; + input logic clk; + input logic in; + signed int [31:0] [0:2] t.array; + logic logic [15:0] t.pubflat; + logic logic [15:0] t.pubflat_r; + int signed int [31:0] t.fd; + int signed int [31:0] t.cyc; + int signed int [31:0] t.fo; + int signed int [31:0] t.sum; + string string t.str; + int signed int [31:0] t._Vpast_0_0; + int signed int [31:0] t._Vpast_1_0; + int signed int [31:0] t.unnamedblk3.i; + @(*)@([settle])@([initial])@(posedge clk)@(negedge + clk)always @( + *)@( + [settle])@( + [initial])@( + posedge + clk)@( + negedge + clk) begin + $display("stmt"); + end + always @([settle])@([initial])@(posedge clk)@(negedge + clk) begin + $display("stmt"); + end + initial begin + // Function: f + $write("stmt\nstmt 0 99\n"); + // Function: t + $display("stmt"); + // Function: f + $write("stmt\nstmt 1 -1\n"); + // Function: t + $display("stmt"); + // Function: f + $display("stmt"); + $display("stmt 2 -2"); + // Function: t + $display("stmt"); + $display("stmt"); + end + + ???? // CFUNC '_final_TOP' + $_CSTMT(Vt_debug_emitv* const __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp; + ); + // FINAL + $display("stmt"); + always @(posedge clk)@(negedge clk) begin + $display("posedge clk"); + end + always @(posedge clk)@(negedge clk) begin + __Vdly__t.pubflat_r <= t.pubflat; + end + always @(posedge clk)@(negedge clk) begin + __Vdly__t.cyc <= (32'sh1 + t.cyc); + t.fo = t.cyc; + // Function: inc + __Vtask_t.sub.inc__2__i = t.fo; + __Vtask_t.sub.inc__2__o = (32'h1 + __Vtask_t.sub.inc__2__i[31:1]); + t.sum = __Vtask_t.sub.inc__2__o; + // Function: f + __Vfunc_t.sub.f__3__v = t.sum; + begin : label0 + begin : label0 + if ((32'sh0 == __Vfunc_t.sub.f__3__v)) begin + __Vfunc_t.sub.f__3__Vfuncout = 32'sh21; + disable label0; + end + __Vfunc_t.sub.f__3__Vfuncout = (32'h1 + + __Vfunc_t.sub.f__3__v[2]); + disable label0; + end + end + t.sum = __Vfunc_t.sub.f__3__Vfuncout; + $display("sum = %~", t.sum); + $c(;); + $display("%d", $c(0)); + $fopen(72'h2f6465762f6e756c6c); + $fclose(t.fd); + $fopen(72'h2f6465762f6e756c6c, 8'h72); + $fgetc(t.fd); + $fflush(t.fd); + $fscanf(t.fd, "%d", t.sum); + ; + $fdisplay(32'h69203d20, "%d", t.sum); + $fwrite(t.fd, "hello"); + $readmemh(t.fd, t.array); + $readmemh(t.fd, t.array, 32'sh0); + $readmemh(t.fd, t.array, 32'sh0, 32'sh0); + t.sum = 32'sh0; + t.unnamedblk3.i = 32'sh0; + begin : label0 + while ((t.unnamedblk3.i < t.cyc)) begin + t.sum = (t.sum + t.unnamedblk3.i); + if ((32'sha < t.sum)) begin + disable label0; + end + else begin + t.sum = (32'sh1 + t.sum); + end + t.unnamedblk3.i = (32'h1 + t.unnamedblk3.i); + end + end + if ((32'sh63 == t.cyc)) begin + $finish; + end + if ((32'sh64 == t.cyc)) begin + $stop; + end + if (in) begin + $display("1"); + end + else begin + $display("default"); + end + if (in) begin + $display("1"); + end + else begin + $display("default"); + end + if (in) begin + $display("1"); + end + else begin + $display("default"); + end + if (in) begin + $display("1"); + end + else begin + $display("default"); + end + if (in) begin + $display("1"); + end + else begin + $display("0"); + end + priority if (in) begin + $display("1"); + end + else begin + $display("0"); + end + unique if (in) begin + $display("1"); + end + else begin + $display("0"); + end + unique0 if (in) begin + $display("1"); + end + else begin + $display("0"); + end + $display("%d%d", t._Vpast_0_0t._Vpast_1_0, + t._Vpast_1_0); + t.str = $sformatf("cyc=%~", t.cyc); + ; + $display("str = %@", t.str); + $display("[%t] [%^]", $time$realtime, $realtime); + end + /*verilator public_flat_rw @(posedge clk)@(negedge + clk) t.pubflat*/ + always @(posedge clk)@(negedge clk) begin + __Vdly__t._Vpast_0_0 <= t.cyc; + end + always @(posedge clk)@(negedge clk) begin + __Vdly__t._Vpast_1_0 <= t.cyc; + end + __Vdly__t._Vpast_1_0 = t._Vpast_1_0; + t._Vpast_1_0 = __Vdly__t._Vpast_1_0; + __Vdly__t._Vpast_0_0 = t._Vpast_0_0; + t._Vpast_0_0 = __Vdly__t._Vpast_0_0; + __Vdly__t.cyc = t.cyc; + t.cyc = __Vdly__t.cyc; + __Vdly__t.pubflat_r = t.pubflat_r; + t.pubflat_r = __Vdly__t.pubflat_r; + always @(negedge clk) begin + $display("negedge clk, pfr = %x", t.pubflat_r); + end + int signed int [31:0] __Vtask_t.sub.inc__2__i; + int signed int [31:0] __Vtask_t.sub.inc__2__o; + int signed int [31:0] __Vfunc_t.sub.f__3__Vfuncout; + int signed int [31:0] __Vfunc_t.sub.f__3__v; + logic logic [15:0] __Vdly__t.pubflat_r; + int signed int [31:0] __Vdly__t.cyc; + int signed int [31:0] __Vdly__t._Vpast_0_0; + int signed int [31:0] __Vdly__t._Vpast_1_0; +endmodule diff --git a/test_regress/t/t_debug_emitv.pl b/test_regress/t/t_debug_emitv.pl new file mode 100755 index 000000000..3532795bc --- /dev/null +++ b/test_regress/t/t_debug_emitv.pl @@ -0,0 +1,22 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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(vlt => 1); + +lint( + # We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions + # Likewise XML + v_flags => ["--lint-only --dump-treei 9 --debug-emitv"], + ); + +files_identical("$Self->{obj_dir}/$Self->{VM_PREFIX}__preorder.v", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v new file mode 100644 index 000000000..073485457 --- /dev/null +++ b/test_regress/t/t_debug_emitv.v @@ -0,0 +1,150 @@ +// DESCRIPTION: Verilator: Dotted reference that uses another dotted reference +// as the select expression +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk, in + ); + input clk; + input in; + + // verilator lint_off UNPACKED + + typedef enum { + ZERO, + ONE = 1 + } e_t; + + typedef struct packed { + e_t a; + } ps_t; + typedef struct { + logic signed [2:0] a; + } us_t; + + const ps_t ps[3]; + us_t us; + + int array[3]; + initial array = '{1,2,3}; + + reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; + + reg [15:0] pubflat_r; + wire [15:0] pubflat_w = pubflat; + int fd; + + task t; + $display("stmt"); + endtask + function int f(input int v); + $display("stmt"); + return v == 0 ? 99 : ~v + 1; + endfunction + + sub sub(); + + initial begin + int other; + begin //unnamed + for (int i = 0; i < 3; ++i) begin + other = f(i); + $display("stmt %d %d", i, other); + t(); + end + end + begin : named + $display("stmt"); + end : named + end + final begin + $display("stmt"); + end + + always @ (in) begin + $display("stmt"); + end + always @ (posedge clk) begin + $display("posedge clk"); + pubflat_r <= pubflat_w; + end + always @ (negedge clk) begin + $display("negedge clk, pfr = %x", pubflat_r); + end + + int cyc; + int fo; + int sum; + string str; + always_ff @ (posedge clk) begin + cyc <= cyc + 1; + fo = cyc; + sub.inc(fo, sum); + sum = sub.f(sum); + $display("sum = %d", sum); + + $c(";"); + $display("%d", $c("0")); + fd = $fopen("/dev/null"); + $fclose(fd); + fd = $fopen("/dev/null", "r"); + $fgetc(fd); // stmt + $fflush(fd); + $fscanf(fd, "%d", sum); + $fdisplay("i = ", sum); + $fwrite(fd, "hello"); + $readmemh(fd, array); + $readmemh(fd, array, 0); + $readmemh(fd, array, 0, 0); + + sum = 0; + for (int i = 0; i < cyc; ++i) begin + sum += i; + if (sum > 10) break; + else sum += 1; + end + if (cyc == 99) $finish; + if (cyc == 100) $stop; + + case (in) // synopsys full_case parallel_case + 1: $display("1"); + default: $display("default"); + endcase + priority case (in) + 1: $display("1"); + default: $display("default"); + endcase + unique case (in) + 1: $display("1"); + default: $display("default"); + endcase + unique0 case (in) + 1: $display("1"); + default: $display("default"); + endcase + if (in) $display("1"); else $display("0"); + priority if (in) $display("1"); else $display("0"); + unique if (in) $display("1"); else $display("0"); + unique0 if (in) $display("1"); else $display("0"); + + $display($past(cyc), $past(cyc, 1)); + + str = $sformatf("cyc=%d", cyc); + $display("str = %s", str); + $display("[%t] [%t]", $time, $realtime); + end +endmodule + +module sub(); + task inc(input int i, output int o); + o = {1'b0, i[31:1]} + 32'd1; + endtask + function int f(input int v); + if (v == 0) return 33; + return {31'd0, v[2]} + 32'd1; + endfunction +endmodule