Fix various round-trip Verilog output, including packed arrays

This commit is contained in:
Wilson Snyder 2025-01-24 21:00:45 -05:00
parent 432d5f851d
commit 929e15fa4c
4 changed files with 233 additions and 172 deletions

View File

@ -28,13 +28,17 @@ VL_DEFINE_DEBUG_FUNCTIONS;
// Emit statements and expressions
class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
// MEMBERS
bool m_suppressSemi = false;
// STATE - across all visitors
const bool m_suppressUnknown = false;
// STATE - for current visit position (use VL_RESTORER)
AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE
bool m_suppressSemi = false; // Non-statement, don't print ;
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
bool m_arrayPost = false; // Print array information that goes after identifier (vs after)
std::deque<AstNodeArrayDType*> m_packedps; // Packed arrays to print with BasicDType
// METHODS
virtual void puts(const string& str) = 0;
virtual void putbs(const string& str) = 0;
virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark
@ -49,6 +53,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putsNoTracking("\"");
}
void iterateAndCommaConstNull(AstNode* nodep) {
for (; nodep; nodep = nodep->nextp()) {
iterateConst(nodep);
if (nodep->nextp()) puts(", ");
}
}
void emitPacked() {
for (AstNodeArrayDType* packedp : m_packedps) {
puts(" ");
iterateConstNull(packedp->rangep());
}
m_packedps.clear();
}
// VISITORS
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
void visit(AstNodeModule* nodep) override {
@ -58,13 +76,14 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstPort* nodep) override {}
void visit(AstNodeFTask* nodep) override {
putfs(nodep, nodep->isFunction() ? "function" : "task");
const bool func = nodep->isFunction() || nodep->name() == "new";
putfs(nodep, func ? "function" : "task");
puts(" ");
puts(nodep->prettyName());
puts(";\n");
// Only putfs the first time for each visitor; later for same node is putqs
iterateAndNextConstNull(nodep->stmtsp());
putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n");
putfs(nodep, func ? "endfunction\n" : "endtask\n");
}
void visit(AstBegin* nodep) override {
@ -169,7 +188,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstSenItem* nodep) override {
putfs(nodep, "");
puts(nodep->edgeType().verilogKwd());
if (nodep->edgeType() != VEdgeType::ET_CHANGED) puts(nodep->edgeType().verilogKwd());
if (nodep->sensp()) puts(" ");
iterateChildrenConst(nodep);
}
@ -221,13 +240,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putfs(nodep, nodep->verilogKwd());
putbs("(");
if (fileOrStrgp) {
iterateAndNextConstNull(fileOrStrgp);
iterateConstNull(fileOrStrgp);
putbs(", ");
}
putsQuoted(text);
for (AstNode* expp = exprsp; expp; expp = expp->nextp()) {
puts(", ");
iterateAndNextConstNull(expp);
iterateConstNull(expp);
}
puts(");\n");
}
@ -393,7 +412,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstTextBlock* nodep) override {
visit(static_cast<AstNodeSimpleText*>(nodep));
VL_RESTORER(m_suppressSemi);
VL_RESTORER(m_suppressVarSemi);
m_suppressVarSemi = nodep->commas();
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
iterateConst(childp);
@ -403,17 +422,17 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
void visit(AstScopeName* nodep) override {}
void visit(AstCStmt* nodep) override {
putfs(nodep, "$_CSTMT(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(");\n");
}
void visit(AstCExpr* nodep) override {
putfs(nodep, "$_CEXPR(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(")");
}
void visit(AstUCStmt* nodep) override {
putfs(nodep, "$c(");
iterateAndNextConstNull(nodep->exprsp());
iterateAndCommaConstNull(nodep->exprsp());
puts(");\n");
}
void visit(AstUCFunc* nodep) override {
@ -431,19 +450,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
void visit(AstCMethodHard* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->pinsp()) puts(", ");
iterateConst(pinp);
}
iterateAndCommaConstNull(nodep->pinsp());
puts(")");
}
void visit(AstCMethodCall* nodep) override {
iterateConst(nodep->fromp());
puts("." + nodep->name() + "(");
for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) {
if (pinp != nodep->argsp()) puts(", ");
iterateConst(pinp);
}
iterateAndCommaConstNull(nodep->argsp());
puts(")");
}
@ -565,13 +578,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
puts(cvtToStr(nodep->leftConst()));
puts(":");
puts(cvtToStr(nodep->rightConst()));
puts("]");
} else {
iterateAndNextConstNull(nodep->leftp());
puts(":");
iterateAndNextConstNull(nodep->rightp());
puts("]");
}
puts("]");
}
void visit(AstRand* nodep) override {
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->seedp());
}
void visit(AstSel* nodep) override {
iterateAndNextConstNull(nodep->fromp());
@ -603,17 +618,31 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
void visit(AstTypedef* nodep) override {
putfs(nodep, "typedef ");
iterateAndNextConstNull(nodep->subDTypep());
iterateConstNull(nodep->subDTypep());
puts(" ");
puts(nodep->prettyName());
puts(";\n");
}
void visit(AstAssocArrayDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
puts("[");
iterateConst(nodep->keyDTypep());
puts("]");
m_arrayPost = true;
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstBasicDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, nodep->prettyName());
if (nodep->isSigned()) putfs(nodep, " signed");
if (nodep->isSigned() && !nodep->keyword().isDouble()) putfs(nodep, " signed");
// Do not emit ranges for integer atoms.
if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return;
emitPacked();
if (nodep->rangep()) {
puts(" ");
iterateAndNextConstNull(nodep->rangep());
@ -627,12 +656,50 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
}
void visit(AstConstDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, "const ");
iterateConst(nodep->subDTypep());
}
void visit(AstNodeArrayDType* nodep) override {
void visit(AstDynArrayDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
puts("[]");
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstEnumDType* nodep) override {
if (m_arrayPost) return;
putfs(nodep, "enum ");
iterateConst(nodep->subDTypep());
iterateAndNextConstNull(nodep->rangep());
puts("{\n");
iterateAndNextConstNull(nodep->itemsp());
puts("}");
}
void visit(AstEnumItem* nodep) override {
putfs(nodep, nodep->name());
iterateConstNull(nodep->rangep());
puts(" = ");
iterateConstNull(nodep->valuep());
if (nodep->nextp()) puts(",");
puts("\n");
}
void visit(AstNodeArrayDType* nodep) override {
if (!m_arrayPost) {
if (VN_IS(nodep, PackArrayDType)) {
// Unpacked ranges handled in BasicDType, as they print "backwards"
m_packedps.push_back(nodep);
}
iterateConst(nodep->subDTypep());
} else {
if (VN_IS(nodep, UnpackArrayDType)) {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
iterateAndNextConstNull(nodep->rangep());
m_arrayPost = true;
}
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstRefDType* nodep) override {
if (nodep->subDTypep()) {
@ -642,21 +709,43 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
}
void visit(AstNodeUOrStructDType* nodep) override {
if (m_arrayPost) return;
puts(nodep->verilogKwd() + " ");
if (nodep->packed()) puts("packed ");
puts("\n");
puts("{");
for (AstMemberDType* itemp = nodep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
iterateConst(itemp);
puts(";");
{
puts("{\n");
VL_RESTORER(m_packedps);
m_packedps.clear();
for (AstMemberDType* itemp = nodep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
iterateConst(itemp);
}
puts("}");
}
puts("}");
emitPacked();
}
void visit(AstMemberDType* nodep) override {
if (m_arrayPost) return;
iterateConst(nodep->subDTypep());
puts(" ");
puts(nodep->name());
puts(";\n");
}
void visit(AstQueueDType* nodep) override {
if (!m_arrayPost) {
iterateConst(nodep->subDTypep());
} else {
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
puts("[$");
if (nodep->boundp()) {
puts(":");
iterateConst(nodep->boundp());
}
puts("]");
m_arrayPost = true;
iterateConst(nodep->subDTypep()); // For post's key
}
}
void visit(AstNodeFTaskRef* nodep) override {
if (nodep->dotted() != "") {
@ -719,37 +808,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
putfs(nodep, nodep->verilogKwd());
puts(" ");
}
std::vector<const AstUnpackArrayDType*> unpackps;
for (AstNodeDType* dtypep = nodep->dtypep(); dtypep;) {
dtypep = dtypep->skipRefp();
if (const AstUnpackArrayDType* const unpackp = VN_CAST(dtypep, UnpackArrayDType)) {
unpackps.push_back(unpackp);
dtypep = unpackp->subDTypep();
} else {
iterateConst(dtypep);
puts(" ");
puts(nodep->prettyName());
dtypep = nullptr;
}
}
// If nodep is an unpacked array, append unpacked dimensions
for (const auto& unpackp : unpackps) {
puts("[");
puts(cvtToStr(unpackp->rangep()->leftConst()));
puts(":");
puts(cvtToStr(unpackp->rangep()->rightConst()));
puts("]");
}
VL_RESTORER(m_arrayPost);
m_arrayPost = false;
iterateConstNull(nodep->dtypep()); // Dtype part before identifier
puts(" ");
puts(nodep->prettyName());
m_arrayPost = true;
iterateConstNull(nodep->dtypep()); // Dtype part after identifier
puts(m_suppressVarSemi ? "\n" : ";\n");
}
void visit(AstActive* nodep) override {
VL_RESTORER(m_sensesp);
m_sensesp = nodep->sensesp();
iterateAndNextConstNull(nodep->stmtsp());
m_sensesp = nullptr;
}
void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
void visit(AstVarScope*) override {}
void visit(AstNodeText*) override {}
void visit(AstVarScope*) override {}
void visit(AstTraceDecl*) override {}
void visit(AstTraceInc*) override {}
// NOPs
@ -767,7 +842,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
}
public:
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp)
: m_suppressUnknown{suppressUnknown}
, m_sensesp{domainp} {}

View File

@ -1,94 +1,65 @@
module Vt_debug_emitv_t;
input logic clk;
input logic in;
typedef
???? // ENUMDTYPE 't.e_t'
???? // ENUMITEM 'ZERO'
32'h0
???? // ENUMITEM 'ONE'
32'h1int signedstruct packed
{int signed a;}logic signed [2:0] struct
{logic signed [2:0] a;}logicunion
{logic a;}struct packed
{int signed a;}bit [31:0] const struct packed
{int signed a;}const struct packed
{int signed a;}[0:2]struct
{logic signed [2:0] a;}union
{logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed
???? // QUEUEDTYPE
int signedstring
???? // ASSOCARRAYDTYPE
int signed
???? // DYNARRAYDTYPE
int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed e_t;
typedef struct packed
{int signed a;}logic signed [2:0] struct
{logic signed [2:0] a;}logicunion
{logic a;}struct packed
{int signed a;}bit [31:0] const struct packed
{int signed a;}const struct packed
{int signed a;}[0:2]struct
{logic signed [2:0] a;}union
{logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed
???? // QUEUEDTYPE
int signedstring
???? // ASSOCARRAYDTYPE
int signed
???? // DYNARRAYDTYPE
int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed ps_t;
typedef struct
{logic signed [2:0] a;}logicunion
{logic a;}struct packed
{int signed a;}bit [31:0] const struct packed
{int signed a;}const struct packed
{int signed a;}[0:2]struct
{logic signed [2:0] a;}union
{logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed
???? // QUEUEDTYPE
int signedstring
???? // ASSOCARRAYDTYPE
int signed
???? // DYNARRAYDTYPE
int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed us_t;
typedef union
{logic a;}struct packed
{int signed a;}bit [31:0] const struct packed
{int signed a;}const struct packed
{int signed a;}[0:2]struct
{logic signed [2:0] a;}union
{logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed
???? // QUEUEDTYPE
int signedstring
???? // ASSOCARRAYDTYPE
int signed
???? // DYNARRAYDTYPE
int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed union_t;
struct packed
{int signed a;} ps[0:2];
struct
{logic signed [2:0] a;} us;
union
{logic a;} unu;
typedef enum logic [2:0] {
ZERO = 3'h0,
ONE = 3'h1
} e_t;
typedef struct packed {
logic [2:0] a;
} ps_t;
typedef struct {
logic signed [2:0] a;
} us_t;
typedef union {
logic a;
} union_t;
const struct packed {
logic [2:0] a;
} ps[0:2];
struct {
logic signed [2:0] a;
} us;
union {
logic a;
} unu;
int signed array[0:2];
initial begin
array = '{0:32'sh1, 1:32'sh2, 2:32'sh3};
end
bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15];
logic [15:0] pubflat;
logic [15:0] pubflat_r;
logic [15:0] pubflat_w;
assign pubflat_w = pubflat;
int signed fd;
int signed i;
???? // QUEUEDTYPE
q;
???? // ASSOCARRAYDTYPE
assoc;
???? // DYNARRAYDTYPE
dyn;
int signed q[$];
int signed qb[$:'sh3];
int signed assoc[string];
int signed assocassoc[string][real];
int signed dyn[];
typedef struct packed {
logic nn1;
} nested_named_t;
typedef struct packed {
struct packed {
logic nn2;
} nested_anonymous;
struct packed {
logic nn1;
} nested_named;
logic [11:10] nn3;
} nibble_t;
struct packed {
struct packed {
logic nn2;
} nested_anonymous;
struct packed {
logic nn1;
} nested_named;
logic [11:10] nn3;
} [5:4] nibblearray[3:2];
task t;
$display("stmt");
endtask
@ -111,7 +82,7 @@ module Vt_debug_emitv_t;
begin
other = f(i);
$display("stmt %~ %~",
iother, other);
i, other);
t();
end
i = (i + 'h1);
@ -128,7 +99,7 @@ module Vt_debug_emitv_t;
$display("stmt");
end
end
always @([changed] in) begin
always @( in) begin
begin
$display("stmt");
end
@ -147,7 +118,7 @@ module Vt_debug_emitv_t;
int signed cyc;
int signed fo;
int signed sum;
real signed r;
real r;
string str;
always @(posedge clk) begin
begin
@ -156,7 +127,7 @@ module Vt_debug_emitv_t;
fo = cyc;
sub.inc(fosum);
sum = sub.f(sum);
$display("[%0t] sum = %~", $timesum, sum);
$display("[%0t] sum = %~", $time, sum);
$display("a?= %d", ($c('sh1) ? $c('sh14)
: $c('sh1e)));
$c(;);
@ -248,32 +219,23 @@ module Vt_debug_emitv_t;
else begin
$display("0");
end
$display("%~%~", $past(cyc)$past(cyc, 'sh1),
$past(cyc, 'sh1));
$display("%~%~", $past(cyc), $past(cyc,
'sh1));
str = $sformatf("cyc=%~", cyc);
;
$display("str = %@", str);
$display("%% [%t] [%^] to=%o td=%d", $time
$realtime$time$time, $realtime
$time$time, $time$time, $time);
$display("%% [%t] [%^] to=%o td=%d", $time,
$realtime, $time, $time);
$sscanf(40'h666f6f3d35, "foo=%d", i);
;
$printtimescale;
if ((i != 'sh5)) begin
$stop;
end
sum =
???? // RAND
;
sum =
???? // RAND
'sha;
sum =
???? // RAND
;
sum =
???? // RAND
'sha;
sum = $random();
sum = $random('sha);
sum = $urandom();
sum = $urandom('sha);
if ((PKG_PARAM != 'sh1)) begin
$stop;
end
@ -318,8 +280,8 @@ package Vt_debug_emitv___024unit;
member = 'sh1;
task method;
endtask
task new;
endtask
function new;
endfunction
endclass
endpackage
module Vt_debug_emitv_sub;
@ -339,7 +301,7 @@ module Vt_debug_emitv_sub;
disable label0;
end
endfunction
real signed r;
real r;
endmodule
package Vt_debug_emitv_p;
logic pkgvar;

View File

@ -16,7 +16,16 @@ test.lint(
# Likewise XML
v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"])
test.files_identical(test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v"),
test.golden_filename)
output_v = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v")
test.files_identical(output_v, test.golden_filename)
if test.verbose:
# Print if that the output Verilog is clean
# TODO not yet round-trip clean
test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator", "--lint-only", output_v],
logfile=test.obj_dir + "/sim_roundtrip.log",
fails=True,
verilator_run=True)
test.passes()

View File

@ -33,7 +33,7 @@ module t (/*AUTOARG*/
// verilator lint_off UNPACKED
typedef enum {
typedef enum [2:0] {
ZERO,
ONE = 1
} e_t;
@ -52,19 +52,35 @@ module t (/*AUTOARG*/
us_t us;
union_t unu;
int array[3];
int array[3];
initial array = '{1,2,3};
reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */;
bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15];
reg [15:0] pubflat_r;
wire [15:0] pubflat_w = pubflat;
int fd;
int i;
reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */;
int q[$];
int assoc[string];
int dyn[];
reg [15:0] pubflat_r;
wire [15:0] pubflat_w = pubflat;
int fd;
int i;
int q[$];
int qb[$ : 3];
int assoc[string];
int assocassoc[string][real];
int dyn[];
typedef struct packed {
logic nn1;
} nested_named_t;
typedef struct packed {
struct packed {
logic nn2;
} nested_anonymous;
nested_named_t nested_named;
logic [11:10] nn3;
} nibble_t;
nibble_t [5:4] nibblearray[3:2];
task t;
$display("stmt");