From a4278608255d7732a98dd35eb4a14b3c35227a08 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 11 Nov 2022 21:53:05 -0500 Subject: [PATCH] Support randcase. --- Changes | 1 + src/V3AstNodeExpr.h | 2 +- src/V3AstNodeOther.h | 10 ++++ src/V3Randomize.cpp | 69 ++++++++++++++++++++++++++++ src/V3Width.cpp | 18 ++++++++ src/verilog.y | 9 +++- test_regress/t/t_randcase.pl | 21 +++++++++ test_regress/t/t_randcase.v | 76 +++++++++++++++++++++++++++++++ test_regress/t/t_randcase_bad.out | 1 + test_regress/t/t_randcase_bad.pl | 21 +++++++++ test_regress/t/t_randcase_bad.v | 18 ++++++++ 11 files changed, 243 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_randcase.pl create mode 100644 test_regress/t/t_randcase.v create mode 100644 test_regress/t/t_randcase_bad.out create mode 100755 test_regress/t/t_randcase_bad.pl create mode 100644 test_regress/t/t_randcase_bad.v diff --git a/Changes b/Changes index e00fffb99..9da2aaabc 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.003 devel * Deprecate --no-threads; use --threads 1 for single threaded (#3703). [Kamil Rakoczy, Antmicro Ltd] * Support named properties (#3667). [Ryszard Rozak, Antmicro Ltd] +* Support randcase. * Internal AST improvements, also affect XML format (#3721). [Geza Lore] * Fix return type of $countbits functions to int (#3725). [Ryszard Rozak, Antmicro Ltd] * Fix missing UNUSED warnings with --coverage (#3736). [alejandro-castro-ortegon] diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 305ac78b8..b1260e062 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -2839,7 +2839,7 @@ public: class AstURandomRange final : public AstNodeBiop { // $urandom_range public: - explicit AstURandomRange(FileLine* fl, AstNode* lhsp, AstNode* rhsp) + AstURandomRange(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : ASTGEN_SUPER_URandomRange(fl, lhsp, rhsp) { dtypeSetUInt32(); // Says IEEE } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index a88aa3ee6..9b52144fe 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3012,6 +3012,16 @@ public: void timeunit(const VTimescale& flag) { m_timeunit = flag; } VTimescale timeunit() const { return m_timeunit; } }; +class AstRandCase final : public AstNodeStmt { + // @astgen op2 := itemsp : List[AstCaseItem] +public: + AstRandCase(FileLine* fl, AstCaseItem* itemsp) + : ASTGEN_SUPER_RandCase(fl) { + addItemsp(itemsp); + } + ASTGEN_MEMBERS_AstRandCase; + int instrCount() const override { return INSTR_COUNT_BRANCH; } +}; class AstRelease final : public AstNodeStmt { // Procedural 'release' statement // @astgen op1 := lhsp : AstNode diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 385d50b3e..22a5074f2 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -100,6 +100,7 @@ private: markMembers(classp); } } + void visit(AstNode* nodep) override { iterateChildren(nodep); } public: @@ -124,7 +125,9 @@ private: const VNUser2InUse m_inuser2; // STATE + AstNodeModule* m_modp = nullptr; // Current module size_t m_enumValueTabCount = 0; // Number of tables with enum values created + int m_randCaseNum = 0; // Randcase number within a module for var naming // METHODS AstVar* enumValueTabp(AstEnumDType* nodep) { @@ -197,7 +200,16 @@ private: } // VISITORS + void visit(AstNodeModule* nodep) override { + VL_RESTORER(m_randCaseNum); + m_modp = nodep; + m_randCaseNum = 0; + iterateChildren(nodep); + } void visit(AstClass* nodep) override { + VL_RESTORER(m_randCaseNum); + m_modp = nodep; + m_randCaseNum = 0; iterateChildren(nodep); if (!nodep->user1()) return; // Doesn't need randomize, or already processed UINFO(9, "Define randomize() for " << nodep << endl); @@ -239,6 +251,63 @@ private: } nodep->user1(false); } + void visit(AstRandCase* nodep) override { + // RANDCASE + // CASEITEM expr1 : stmt1 + // CASEITEM expr2 : stmt2 + // -> + // tmp = URandomRange{0, num} + 1 // + 1 so weight 0 means never + // if (tmp < expr1) stmt1; + // else if (tmp < (expr2 + expr1)) stmt1; + // else warning + // Note this code assumes that the expressions after V3Const are fast to compute + // Optimize: we would be better with a binary search tree to reduce ifs that execute + if (debug() >= 9) nodep->dumpTree(cout, "-rcin: "); + AstNodeDType* const sumDTypep = nodep->findUInt64DType(); + + FileLine* const fl = nodep->fileline(); + const std::string name = "__Vrandcase" + cvtToStr(m_randCaseNum++); + AstVar* const randVarp = new AstVar{fl, VVarType::STMTTEMP, name, sumDTypep}; + randVarp->noSubst(true); + AstNodeExpr* sump = new AstConst{fl, AstConst::WidthedValue{}, 64, 0}; + AstNodeIf* firstIfsp + = new AstIf{fl, new AstConst{fl, AstConst::BitFalse{}}, nullptr, nullptr}; + AstNodeIf* ifsp = firstIfsp; + + for (AstCaseItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), CaseItem)) { + AstNode* const condp = itemp->condsp()->unlinkFrBack(); + sump + = new AstAdd{condp->fileline(), sump, new AstExtend{itemp->fileline(), condp, 64}}; + AstNode* const stmtsp + = itemp->stmtsp() ? itemp->stmtsp()->unlinkFrBackWithNext() : nullptr; + AstNodeIf* const newifp + = new AstIf{itemp->fileline(), + new AstLte{condp->fileline(), + new AstVarRef{condp->fileline(), randVarp, VAccess::READ}, + sump->cloneTree(true)}, + stmtsp, nullptr}; + ifsp->addElsesp(newifp); + ifsp = newifp; + } + AstDisplay* dispp = new AstDisplay{ + fl, VDisplayType::DT_ERROR, "All randcase items had 0 weights (IEEE 1800-2017 18.16)", + nullptr, nullptr}; + UASSERT_OBJ(m_modp, nodep, "randcase not under module"); + dispp->fmtp()->timeunit(m_modp->timeunit()); + ifsp->addElsesp(dispp); + + AstNode* newp = randVarp; + AstNode* randp = new AstRand{fl, nullptr, false}; + randp->dtypeSetUInt64(); + newp->addNext(new AstAssign{fl, new AstVarRef{fl, randVarp, VAccess::WRITE}, + new AstAdd{fl, new AstConst{fl, AstConst::Unsized64{}, 1}, + new AstModDiv{fl, randp, sump}}}); + newp->addNext(firstIfsp); + if (debug() >= 9) newp->dumpTreeAndNext(cout, "-rcnew: "); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstNode* nodep) override { iterateChildren(nodep); } public: diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8bbb422ca..758b919fa 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4087,6 +4087,24 @@ private: } } } + void visit(AstRandCase* nodep) override { + // IEEE says each item is a int (32-bits), and sizes are based on natural sizing, + // but we'll sum to a 64-bit number then math is faster. + assertAtStatement(nodep); + v3Global.useRandomizeMethods(true); + AstNodeDType* const itemDTypep = nodep->findUInt32DType(); + for (AstCaseItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) { + nextip = VN_AS(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced + userIterateAndNext(itemp->stmtsp(), nullptr); + for (AstNode *nextcp, *condp = itemp->condsp(); condp; condp = nextcp) { + nextcp = condp->nextp(); // Prelim may cause the node to get replaced + iterateCheckTyped(itemp, "Randcase Item", condp, itemDTypep, BOTH); + VL_DANGLING(condp); // Might have been replaced + } + VL_DANGLING(itemp); // Might have been replaced + } + } + void visit(AstNodeFor* nodep) override { assertAtStatement(nodep); userIterateAndNext(nodep->initsp(), nullptr); diff --git a/src/verilog.y b/src/verilog.y index 1367e140d..5e112eae8 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3369,8 +3369,7 @@ statement_item: // IEEE: statement_item //UNSUP randsequence_statement { $$ = $1; } // // // IEEE: randcase_statement - | yRANDCASE case_itemList yENDCASE - { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 randcase statements"); } + | yRANDCASE rand_case_itemList yENDCASE { $$ = new AstRandCase{$1, $2}; } // //UNSUP expect_property_statement { $$ = $1; } // @@ -3527,6 +3526,12 @@ case_inside_itemList: // IEEE: { case_inside_item + open_range | case_inside_itemList yDEFAULT colon stmtBlock { $$ = $1->addNext(new AstCaseItem{$2, nullptr, $4}); } ; +rand_case_itemList: // IEEE: { rand_case_item + ... } + // // Randcase syntax doesn't have default, or expression lists + expr colon stmtBlock { $$ = new AstCaseItem{$2, $1, $3}; } + | rand_case_itemList expr colon stmtBlock { $$ = $1->addNext(new AstCaseItem{$3, $2, $4}); } + ; + open_range_list: // ==IEEE: open_range_list + open_value_range open_value_range { $$ = $1; } | open_range_list ',' open_value_range { $$ = $1->addNext($3); } diff --git a/test_regress/t/t_randcase.pl b/test_regress/t/t_randcase.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_randcase.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 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(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randcase.v b/test_regress/t/t_randcase.v new file mode 100644 index 000000000..331a53aa0 --- /dev/null +++ b/test_regress/t/t_randcase.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); $stop; end while(0); + +module t (/*AUTOARG*/); + + localparam int COUNT = 1000; + + int v; + int counts[8]; + + initial begin; + + // + for (int i = 0; i < 8; ++i) counts[i] = 0; + for (int i = 0; i < COUNT; ++i) begin + randcase + 0 : ; // Never + 0 : counts[0]++; // Never + 1 : counts[1]++; + endcase + end + `check_range(counts[0], 0, 0); + `check_range(counts[1], COUNT, COUNT); + + // + for (int i = 0; i < 8; ++i) counts[i] = 0; + for (int i = 0; i < COUNT; ++i) begin + randcase + i - i : counts[0]++; // Never + i + i + 1: counts[1]++; + endcase + end + `check_range(counts[0], 0, 0); + `check_range(counts[1], COUNT, COUNT); + + // + for (int i = 0; i < 8; ++i) counts[i] = 0; + for (int i = 0; i < COUNT; ++i) begin + randcase + 1 : counts[0]++; // Never + 4 : counts[1]++; + endcase + end + `check_range(counts[0], (COUNT * 1 / 5) * 70 / 100, (COUNT * 1 / 5) * 130 / 100); + `check_range(counts[1], (COUNT * 4 / 5) * 70 / 100, (COUNT * 4 / 5) * 130 / 100); + + // + for (int i = 0; i < 8; ++i) counts[i] = 0; + for (int i = 0; i < COUNT; ++i) begin + randcase + 2 : counts[0]++; // Never + 2 : counts[1]++; // Never + 1 : counts[2]++; // Never + 1 : counts[3]++; // Never + 1 : counts[4]++; // Never + 1 : counts[5]++; // Never + 1 : counts[6]++; // Never + 1 : counts[7]++; // Never + endcase + end + `check_range(counts[0], (COUNT * 2 / 10) * 70 / 100, (COUNT * 2 / 10) * 130 / 100); + `check_range(counts[1], (COUNT * 2 / 10) * 70 / 100, (COUNT * 2 / 10) * 130 / 100); + `check_range(counts[2], (COUNT * 1 / 10) * 70 / 100, (COUNT * 1 / 10) * 130 / 100); + `check_range(counts[7], (COUNT * 1 / 10) * 70 / 100, (COUNT * 1 / 10) * 130 / 100); + + // + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_randcase_bad.out b/test_regress/t/t_randcase_bad.out new file mode 100644 index 000000000..fa2e9ec7f --- /dev/null +++ b/test_regress/t/t_randcase_bad.out @@ -0,0 +1 @@ +[0] %Error: t_randcase_bad.v:12: Assertion failed in top.t.unnamedblk1: All randcase items had 0 weights (IEEE 1800-2017 18.16) diff --git a/test_regress/t/t_randcase_bad.pl b/test_regress/t/t_randcase_bad.pl new file mode 100755 index 000000000..e988c6264 --- /dev/null +++ b/test_regress/t/t_randcase_bad.pl @@ -0,0 +1,21 @@ +#!/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); + +compile( + ); + +execute( + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randcase_bad.v b/test_regress/t/t_randcase_bad.v new file mode 100644 index 000000000..a299b04e1 --- /dev/null +++ b/test_regress/t/t_randcase_bad.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Test of select from constant +// +// This tests issue 508, bit select of constant fails +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + initial begin + randcase // Bad all zero weights + 0 : $stop; + endcase + $finish; + end + +endmodule