From 70112438c3505c3e73af5d6b8c48eeceaae21f63 Mon Sep 17 00:00:00 2001 From: Yilou Wang <140522618+YilouWang@users.noreply.github.com> Date: Fri, 20 Sep 2024 02:07:05 +0200 Subject: [PATCH] Support packed/unpacked and dynamic array unconstrained randomization (#5414) (#5415) --- src/V3Randomize.cpp | 69 +++++++-- test_regress/t/t_randomize_array.py | 21 +++ test_regress/t/t_randomize_array.v | 145 ++++++++++++++++++ test_regress/t/t_randomize_array_unsup.out | 8 + test_regress/t/t_randomize_array_unsup.py | 16 ++ test_regress/t/t_randomize_array_unsup.v | 72 +++++++++ .../t/t_randomize_method_types_unsup.out | 16 +- .../t/t_randomize_method_types_unsup.v | 1 - test_regress/t/t_uvm_pkg_todo.vh | 12 +- 9 files changed, 325 insertions(+), 35 deletions(-) create mode 100755 test_regress/t/t_randomize_array.py create mode 100755 test_regress/t/t_randomize_array.v create mode 100644 test_regress/t/t_randomize_array_unsup.out create mode 100755 test_regress/t/t_randomize_array_unsup.py create mode 100644 test_regress/t/t_randomize_array_unsup.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 0e43b1cee..2edf86fe0 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1344,11 +1344,54 @@ class RandomizeVisitor final : public VNVisitor { UINFO(9, "created " << varp << endl); return newp; } + AstNodeStmt* createArrayForeachLoop(FileLine* const fl, AstNodeDType* const dtypep, + AstNodeExpr* exprp) { + V3UniqueNames* uniqueNamep = new V3UniqueNames{"__Vrandarr"}; + AstNodeDType* tempDTypep = dtypep; + AstVar* randLoopIndxp = nullptr; + AstNodeStmt* stmtsp = nullptr; + auto createLoopIndex = [&](AstNodeDType* tempDTypep) { + return new AstVar{fl, VVarType::VAR, uniqueNamep->get(""), + dtypep->findBasicDType(VBasicDTypeKwd::UINT32)}; + }; + auto handleUnsupportedStruct = [&](AstNodeExpr* tempElementp) { + if (VN_IS(tempElementp->dtypep()->skipRefp(), StructDType)) { + tempElementp->dtypep()->v3warn( + E_UNSUPPORTED, + "Unsupported: CreateArrayForeachLoop currently does not support " + "this data type. (Struct-Array unconstrained " + "randomization is not fully supported)"); + } + }; + auto createForeachLoop = [&](AstNodeExpr* tempElementp, AstVar* randLoopIndxp) { + AstSelLoopVars* const randLoopVarp + = new AstSelLoopVars{fl, exprp->cloneTree(false), randLoopIndxp}; + return new AstForeach{fl, randLoopVarp, newRandStmtsp(fl, tempElementp, nullptr)}; + }; + AstNodeExpr* tempElementp = nullptr; + while (VN_CAST(tempDTypep, DynArrayDType) || VN_CAST(tempDTypep, UnpackArrayDType)) { + AstVar* const newRandLoopIndxp = createLoopIndex(tempDTypep); + randLoopIndxp = AstNode::addNext(randLoopIndxp, newRandLoopIndxp); + tempElementp + = VN_CAST(tempDTypep, DynArrayDType) + ? static_cast( + new AstCMethodHard{fl, tempElementp ? tempElementp : exprp, "atWrite", + new AstVarRef{fl, newRandLoopIndxp, VAccess::READ}}) + : static_cast( + new AstArraySel{fl, tempElementp ? tempElementp : exprp, + new AstVarRef{fl, newRandLoopIndxp, VAccess::READ}}); + tempElementp->dtypep(tempDTypep->subDTypep()); + tempDTypep = tempDTypep->virtRefDTypep(); + } + handleUnsupportedStruct(tempElementp); + stmtsp = createForeachLoop(tempElementp, randLoopIndxp); + return stmtsp; + } AstNodeStmt* newRandStmtsp(FileLine* fl, AstNodeExpr* exprp, AstVar* randcVarp, int offset = 0, AstMemberDType* memberp = nullptr) { - if (const auto* const structDtp - = VN_CAST(memberp ? memberp->subDTypep()->skipRefp() : exprp->dtypep()->skipRefp(), - StructDType)) { + AstNodeDType* const memberDtp + = memberp ? memberp->subDTypep()->skipRefp() : exprp->dtypep()->skipRefp(); + if (const auto* const structDtp = VN_CAST(memberDtp, StructDType)) { AstNodeStmt* stmtsp = nullptr; if (structDtp->packed()) offset += memberp ? memberp->lsb() : 0; for (AstMemberDType* smemberp = structDtp->membersp(); smemberp; @@ -1364,16 +1407,10 @@ class RandomizeVisitor final : public VNVisitor { if (!structSelp->dtypep()) structSelp->dtypep(smemberp->subDTypep()); randp = newRandStmtsp(fl, structSelp, nullptr); } - if (stmtsp) { - stmtsp->addNext(randp); - } else { - stmtsp = randp; - } + stmtsp = stmtsp ? stmtsp->addNext(randp) : randp; } return stmtsp; - } else if (const auto* const unionDtp = VN_CAST(memberp ? memberp->subDTypep()->skipRefp() - : exprp->dtypep()->skipRefp(), - UnionDType)) { + } else if (const auto* const unionDtp = VN_CAST(memberDtp, UnionDType)) { if (!unionDtp->packed()) { unionDtp->v3error("Unpacked unions shall not be declared as rand or randc." " (IEEE 1800-2023 18.4)"); @@ -1381,6 +1418,11 @@ class RandomizeVisitor final : public VNVisitor { } AstMemberDType* const firstMemberp = unionDtp->membersp(); return newRandStmtsp(fl, exprp, nullptr, offset, firstMemberp); + } else if (AstDynArrayDType* const dynarrayDtp = VN_CAST(memberDtp, DynArrayDType)) { + return createArrayForeachLoop(fl, dynarrayDtp, exprp); + } else if (AstUnpackArrayDType* const unpackarrayDtp + = VN_CAST(memberDtp, UnpackArrayDType)) { + return createArrayForeachLoop(fl, unpackarrayDtp, exprp); } else { AstNodeExpr* valp; if (AstEnumDType* const enumDtp = VN_CAST(memberp ? memberp->subDTypep()->subDTypep() @@ -1561,11 +1603,12 @@ class RandomizeVisitor final : public VNVisitor { if (memberVarp->user3()) return; // Handled in constraints const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp(); if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType) - || VN_IS(dtypep, UnionDType)) { + || VN_IS(dtypep, UnionDType) || VN_IS(dtypep, PackArrayDType) + || VN_IS(dtypep, UnpackArrayDType) || VN_IS(dtypep, DynArrayDType)) { AstVar* const randcVarp = newRandcVarsp(memberVarp); AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE}; AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp); - basicRandomizep->addStmtsp(stmtp); + basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp}); } else if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) { if (classRefp->classp() == nodep) { memberVarp->v3warn(E_UNSUPPORTED, diff --git a/test_regress/t/t_randomize_array.py b/test_regress/t/t_randomize_array.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_array.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_array.v b/test_regress/t/t_randomize_array.v new file mode 100755 index 000000000..58ffb0ac4 --- /dev/null +++ b/test_regress/t/t_randomize_array.v @@ -0,0 +1,145 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field) \ +begin \ + longint prev_result; \ + int ok = 0; \ + for (int i = 0; i < 10; i++) begin \ + longint result; \ + void'(cl.randomize()); \ + result = longint'(field); \ + if (i > 0 && result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class unconstrained_packed_array_test; + + rand bit [3:0] [2:0] [15:0] packed_array; + function new(); + packed_array = '{default: '{default: '{default: 'h0}}}; + endfunction + + function void check_randomization(); + `check_rand(this, this.packed_array) + endfunction + +endclass + +class unconstrained_unpacked_array_test; + + rand bit [2:0] [15:0] unpacked_array [3][5]; + function new(); + unpacked_array = '{ '{default: '{default: 'h0}}, + '{default: '{default: 'h1}}, + '{default: '{default: 'h2}}}; + endfunction + + function void check_randomization(); + foreach (unpacked_array[i]) begin + foreach (unpacked_array[i][j]) begin + // At the innermost packed level, invoke check_rand + `check_rand(this, this.unpacked_array[i][j]) + end + end + endfunction + +endclass + +class unconstrained_dynamic_array_test; + + rand int dynamic_array_1d[]; + rand int dynamic_array_2d[][]; + + function new(); + // Initialize 1D dynamic array + dynamic_array_1d = new[5]; + foreach(dynamic_array_1d[i]) begin + dynamic_array_1d[i] = 'h0 + i; + end + + // Initialize 2D dynamic array + dynamic_array_2d = new[3]; + foreach(dynamic_array_2d[i]) begin + dynamic_array_2d[i] = new[3]; + foreach(dynamic_array_2d[i][j]) begin + dynamic_array_2d[i][j] = 'h0 + i + j; + end + end + endfunction + + function void check_randomization(); + foreach (dynamic_array_1d[i]) begin + `check_rand(this, dynamic_array_1d[i]) + end + foreach (dynamic_array_2d[i]) begin + foreach (dynamic_array_2d[i][j]) begin + `check_rand(this, dynamic_array_2d[i][j]) + end + end + endfunction + +endclass + +class unconstrained_struct_with_array_test; + + typedef struct { + rand bit [7:0] byte_array[4]; + } struct_with_array_t; + + rand struct_with_array_t struct_with_array; + + function new(); + struct_with_array = '{'{default: 'h0}}; + endfunction + + function void check_randomization(); + foreach (struct_with_array.byte_array[i]) begin + `check_rand(this, struct_with_array.byte_array[i]) + end + endfunction + +endclass + +module t_randomize_array; + unconstrained_packed_array_test packed_class; + unconstrained_unpacked_array_test unpacked_class; + unconstrained_dynamic_array_test dynamic_class; + unconstrained_struct_with_array_test struct_with_array_class; + + initial begin + // Test 1: Packed Array Unconstrained Constrained Test + packed_class = new(); + repeat(2) begin + packed_class.check_randomization(); + end + + // Test 2: Unpacked Array Unconstrained Constrained Test + unpacked_class = new(); + repeat(2) begin + unpacked_class.check_randomization(); + end + + // Test 3: Dynamic Array Unconstrained Constrained Test + dynamic_class = new(); + repeat(2) begin + dynamic_class.check_randomization(); + end + + // Test 4: Struct Containing Array Test + + struct_with_array_class = new(); + repeat(2) begin + struct_with_array_class.check_randomization(); + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_randomize_array_unsup.out b/test_regress/t/t_randomize_array_unsup.out new file mode 100644 index 000000000..e65a4c03c --- /dev/null +++ b/test_regress/t/t_randomize_array_unsup.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_randomize_array_unsup.v:28:8: Unsupported: CreateArrayForeachLoop currently does not support this data type. (Struct-Array unconstrained randomization is not fully supported) + 28 | rand simple_struct_t struct_array_2[]; + | ^~~~~~~~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_randomize_array_unsup.v:29:8: Unsupported: CreateArrayForeachLoop currently does not support this data type. (Struct-Array unconstrained randomization is not fully supported) + 29 | rand simple_struct_t struct_array_1[3]; + | ^~~~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_array_unsup.py b/test_regress/t/t_randomize_array_unsup.py new file mode 100755 index 000000000..710a094ab --- /dev/null +++ b/test_regress/t/t_randomize_array_unsup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_array_unsup.v b/test_regress/t/t_randomize_array_unsup.v new file mode 100644 index 000000000..bd140cf81 --- /dev/null +++ b/test_regress/t/t_randomize_array_unsup.v @@ -0,0 +1,72 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field) \ +begin \ + longint prev_result; \ + int ok = 0; \ + for (int i = 0; i < 10; i++) begin \ + longint result; \ + void'(cl.randomize()); \ + result = longint'(field); \ + if (i > 0 && result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class unconstrained_struct_array_test; + + typedef struct { + rand int field_a; + rand int field_b; + } simple_struct_t; + + rand simple_struct_t struct_array_2[]; // Dynamic array + rand simple_struct_t struct_array_1[3]; // Unpacked array + + + function new(); + struct_array_1 = '{'{default: 0}, '{default: 1}, '{default: 2}}; + + struct_array_2 = new[3]; + foreach (struct_array_2[i]) begin + struct_array_2[i].field_a = i; + struct_array_2[i].field_b = i + 1; + end + endfunction + + function void check_randomization(); + foreach (struct_array_1[i]) begin + `check_rand(this, struct_array_1[i].field_a) + `check_rand(this, struct_array_1[i].field_b) + end + + foreach (struct_array_2[i]) begin + `check_rand(this, struct_array_2[i].field_a) + `check_rand(this, struct_array_2[i].field_b) + end + endfunction + +endclass + +module t_randomize_array_unsup; + + unconstrained_struct_array_test struct_array_class; + + initial begin + + // Test: Struct Array Unconstrained Constrained Test + struct_array_class = new(); + repeat(2) begin + struct_array_class.check_randomization(); + end + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_randomize_method_types_unsup.out b/test_regress/t/t_randomize_method_types_unsup.out index d7863c9f1..165fc83d1 100644 --- a/test_regress/t/t_randomize_method_types_unsup.out +++ b/test_regress/t/t_randomize_method_types_unsup.out @@ -1,5 +1,5 @@ -%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:14:32: Unsupported: randomizing this expression, treating as state - 14 | constraint dynsize { dynarr.size < 20; } +%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:13:32: Unsupported: randomizing this expression, treating as state + 13 | constraint dynsize { dynarr.size < 20; } | ^~~~ ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. @@ -7,16 +7,8 @@ : ... note: In instance 't' 8 | rand int assocarr[string]; | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:9:13: Unsupported: random member variable with type 'int$[]' - : ... note: In instance 't' - 9 | rand int dynarr[]; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:10:13: Unsupported: random member variable with type 'int$[0:4]' +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:10:13: Unsupported: random member variable with the type of the containing class : ... note: In instance 't' - 10 | rand int unpackarr[5]; - | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:11:13: Unsupported: random member variable with the type of the containing class - : ... note: In instance 't' - 11 | rand Cls cls; + 10 | rand Cls cls; | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize_method_types_unsup.v b/test_regress/t/t_randomize_method_types_unsup.v index ba3d18f6d..8fbb96f52 100644 --- a/test_regress/t/t_randomize_method_types_unsup.v +++ b/test_regress/t/t_randomize_method_types_unsup.v @@ -7,7 +7,6 @@ class Cls; rand int assocarr[string]; rand int dynarr[]; - rand int unpackarr[5]; rand Cls cls; rand int i; int st; diff --git a/test_regress/t/t_uvm_pkg_todo.vh b/test_regress/t/t_uvm_pkg_todo.vh index e0c8d49ce..888258fa2 100644 --- a/test_regress/t/t_uvm_pkg_todo.vh +++ b/test_regress/t/t_uvm_pkg_todo.vh @@ -21072,21 +21072,15 @@ typedef class uvm_tlm_extension_base; class uvm_tlm_generic_payload extends uvm_sequence_item; rand bit [63:0] m_address; rand uvm_tlm_command_e m_command; - //TODO issue-4625 - Rand fields of dynamic array types - //TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:21081:35: Unsupported: random member variable with type 'byte[]' - /*TODO rand*/ byte unsigned m_data[]; + rand byte unsigned m_data[]; rand int unsigned m_length; rand uvm_tlm_response_status_e m_response_status; bit m_dmi; - //TODO issue-4625 - Rand fields of dynamic array types - //TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:21081:35: Unsupported: random member variable with type 'byte[]' - /*TODO rand*/ byte unsigned m_byte_enable[]; + rand byte unsigned m_byte_enable[]; rand int unsigned m_byte_enable_length; rand int unsigned m_streaming_width; protected uvm_tlm_extension_base m_extensions [uvm_tlm_extension_base]; - //TODO issue-4625 - Rand fields of dynamic array types - //TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:21081:35: Unsupported: random member variable with type 'CLASSREFDTYPE 'uvm_tlm_extension_base'[]' - local /*rand*/ uvm_tlm_extension_base m_rand_exts[]; + local rand uvm_tlm_extension_base m_rand_exts[]; typedef uvm_object_registry#(uvm_tlm_generic_payload,"uvm_tlm_generic_payload") type_id; static function uvm_tlm_generic_payload type_id_create (string name="", uvm_component parent=null,