From 43377ed8b08df09163e6c174ea73592fb3347c99 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Fri, 19 Jul 2024 09:14:56 +0200 Subject: [PATCH] Add support for `this.randomize with` (#5282) Signed-off-by: Arkadiusz Kozdra --- src/V3Randomize.cpp | 40 ++++++++++++++---------- src/V3Width.cpp | 13 +++++++- test_regress/t/t_randomize_method_with.v | 15 +++++++++ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 1520edea9..996f58e2f 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -898,25 +898,33 @@ class RandomizeVisitor final : public VNVisitor { nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } - void visit(AstMethodCall* nodep) override { + void visit(AstNodeFTaskRef* nodep) override { AstWith* const withp = VN_CAST(nodep->pinsp(), With); if (!(nodep->name() == "randomize") || !withp) { iterateChildren(nodep); return; } + withp->unlinkFrBack(); iterateChildren(nodep); - UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Object dtype is not linked"); - AstClassRefDType* const classrefdtypep = VN_CAST(nodep->fromp()->dtypep(), ClassRefDType); - if (!classrefdtypep) { - nodep->v3warn(E_UNSUPPORTED, - "Inline constraints are not supported for this node type"); - return; + AstClass* classp = nullptr; + if (AstMethodCall* const callp = VN_CAST(nodep, MethodCall)) { + UASSERT_OBJ(callp->fromp()->dtypep(), callp->fromp(), "Object dtype is not linked"); + AstClassRefDType* const classrefdtypep + = VN_CAST(callp->fromp()->dtypep(), ClassRefDType); + if (!classrefdtypep) { + nodep->v3warn(E_UNSUPPORTED, + "Inline constraints are not supported for this node type"); + return; + } + classp = classrefdtypep->classp(); + UASSERT_OBJ(classp, classrefdtypep, "Class type is unlinked to its ref type"); + } else { + classp = VN_CAST(m_modp, Class); + UASSERT_OBJ(classp, m_modp, "Module not class, should have failed in V3Width"); } - AstClass* const classp = classrefdtypep->classp(); - UASSERT_OBJ(classp, classrefdtypep, "Class type is unlinked to its ref type"); if (classp->user1()) { // We need to first ensure that the class randomizer is instantiated if needed // NOTE: This is safe only because AstClass visit function overwrites all @@ -985,15 +993,13 @@ class RandomizeVisitor final : public VNVisitor { solverCallp}); // Replace the node with a call to that function - AstMethodCall* const callp - = new AstMethodCall(nodep->fileline(), nodep->fromp()->unlinkFrBack(), - randomizeFuncp->name(), captured.getArgs()); - callp->taskp(randomizeFuncp); - callp->dtypeFrom(randomizeFuncp->dtypep()); - callp->classOrPackagep(classp); - nodep->replaceWith(callp); + nodep->name(randomizeFuncp->name()); + nodep->addPinsp(captured.getArgs()); + nodep->taskp(randomizeFuncp); + nodep->dtypeFrom(randomizeFuncp->dtypep()); + nodep->classOrPackagep(classp); UINFO(9, "Added `%s` randomization procedure"); - VL_DO_DANGLING(nodep->deleteTree(), nodep); + VL_DO_DANGLING(withp->deleteTree(), withp); } void visit(AstNode* nodep) override { iterateChildren(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index da0f99c14..5bb1ad3c4 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3132,7 +3132,7 @@ class WidthVisitor final : public VNVisitor { << nodep->fromp()->dtypep()->prettyTypeName() << "'"); } } - AstWith* methodWithArgument(AstMethodCall* nodep, bool required, bool arbReturn, + AstWith* methodWithArgument(AstNodeFTaskRef* nodep, bool required, bool arbReturn, AstNodeDType* returnDtp, AstNodeDType* indexDtp, AstNodeDType* valueDtp) { UASSERT_OBJ(arbReturn || returnDtp, nodep, "Null return type"); @@ -5990,6 +5990,7 @@ class WidthVisitor final : public VNVisitor { // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign // Function hasn't been widthed, so make it so. UINFO(5, " FTASKREF " << nodep << endl); + AstWith* withp = nullptr; if (nodep->name() == "randomize" || nodep->name() == "srandom" || (!nodep->taskp() && (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) { @@ -5998,6 +5999,15 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot"); if (nodep->name() == "randomize") { nodep->taskp(V3Randomize::newRandomizeFunc(m_memberMap, classp)); + AstClassRefDType* const adtypep + = new AstClassRefDType{nodep->fileline(), classp, nullptr}; + v3Global.rootp()->typeTablep()->addTypesp(adtypep); + withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(), + adtypep->findBitDType(), adtypep); + if (nodep->pinsp()) { + nodep->pinsp()->v3warn(CONSTRAINTIGN, "rand_mode ignored (unsupported)"); + nodep->pinsp()->unlinkFrBackWithNext()->deleteTree(); + } } else if (nodep->name() == "srandom") { nodep->taskp(V3Randomize::newSRandomFunc(m_memberMap, classp)); m_memberMap.clear(); @@ -6041,6 +6051,7 @@ class WidthVisitor final : public VNVisitor { userIterate(nodep->taskp(), nullptr); // And do the arguments to the task/function too processFTaskRefArgs(nodep); + nodep->addPinsp(withp); nodep->didWidth(true); } void visit(AstNodeProcedure* nodep) override { diff --git a/test_regress/t/t_randomize_method_with.v b/test_regress/t/t_randomize_method_with.v index 4521d3601..646a1fbc5 100644 --- a/test_regress/t/t_randomize_method_with.v +++ b/test_regress/t/t_randomize_method_with.v @@ -30,6 +30,9 @@ class Foo extends Boo; endfunction constraint constr1_c { b < x; } + function bit test_this_randomize; + return this.randomize() with { a <= boo; } == 1; + endfunction endclass // Current AstWith representation makes VARs of caller indistinguishable from VARs of randomized @@ -55,6 +58,13 @@ class Baz; rand int v; endclass +class Baz2; + rand int v; + function bit test_this_randomize; + return this.randomize() with { v == 5; } == 1; + endfunction +endclass + module submodule(); int sub_var = 7; endmodule @@ -75,6 +85,7 @@ module mwith(); int c = 30; Foo foo = new(c); Baz baz = new; + Baz2 baz2 = new; Bar bar = new; $display("foo.x = %d", foo.x); $display("-----------------"); @@ -105,6 +116,10 @@ module mwith(); if (!bar.test_capture_of_callers_derived_var(foo)) $stop; // Check randomization with non-captured non-static variable from different AstNodeModule if (!Bar::test_capture_of_callees_derived_var(foo)) $stop; + // Check this.randomize() + if (!foo.test_this_randomize()) $stop; + // Check this.randomize() with no constraints + if (!baz2.test_this_randomize()) $stop; $write("*-* All Finished *-*\n"); $finish();