functionality done, tiny test passed
This commit is contained in:
parent
78ee787bb1
commit
da3d1d9584
|
|
@ -1244,7 +1244,7 @@ class AstDist final : public AstNodeExpr {
|
|||
// @astgen op2 := itemsp : List[AstDistItem]
|
||||
public:
|
||||
AstDist(FileLine* fl, AstNodeExpr* exprp, AstDistItem* itemsp)
|
||||
: ASTGEN_SUPER_Inside(fl) {
|
||||
: ASTGEN_SUPER_Dist(fl) {
|
||||
this->exprp(exprp);
|
||||
addItemsp(itemsp);
|
||||
dtypeSetBit();
|
||||
|
|
|
|||
|
|
@ -2290,6 +2290,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstDynArrayDType* m_dynarrayDtp = nullptr; // Dynamic array type (for rand mode)
|
||||
size_t m_enumValueTabCount = 0; // Number of tables with enum values created
|
||||
int m_randCaseNum = 0; // Randcase number within a module for var naming
|
||||
int m_distNum = 0; // Dist bucket variable counter within a module for var naming
|
||||
std::map<std::string, AstCDType*> m_randcDtypes; // RandC data type deduplication
|
||||
AstConstraint* m_constraintp = nullptr; // Current constraint
|
||||
std::set<std::string> m_writtenVars; // Track write_var calls per class to avoid duplicates
|
||||
|
|
@ -3328,6 +3329,132 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Lower dist constraints into weighted bucket selection + AstConstraintIf chains.
|
||||
// Called for each constraint before ConstraintExprVisitor.
|
||||
// taskp: constraint setup task (bucket var declarations + assignments go here)
|
||||
// constrItemsp: first item in the constraint item list
|
||||
void lowerDistConstraints(AstTask* taskp, AstNode* constrItemsp) {
|
||||
for (AstNode *nextip, *itemp = constrItemsp; itemp; itemp = nextip) {
|
||||
nextip = itemp->nextp();
|
||||
AstConstraintExpr* const constrExprp = VN_CAST(itemp, ConstraintExpr);
|
||||
if (!constrExprp) continue;
|
||||
AstDist* const distp = VN_CAST(constrExprp->exprp(), Dist);
|
||||
if (!distp) continue;
|
||||
|
||||
FileLine* const fl = distp->fileline();
|
||||
|
||||
// Collect buckets and compute effective weights
|
||||
struct BucketInfo {
|
||||
AstNodeExpr* rangep; // Points into distItem (will be cloned, not unlinked)
|
||||
uint64_t effectiveWeight;
|
||||
};
|
||||
std::vector<BucketInfo> buckets;
|
||||
uint64_t totalWeight = 0;
|
||||
|
||||
for (AstDistItem* ditemp = distp->itemsp(); ditemp;
|
||||
ditemp = VN_AS(ditemp->nextp(), DistItem)) {
|
||||
const AstConst* const weightp = VN_CAST(ditemp->weightp(), Const);
|
||||
if (!weightp) continue; // Non-const weight: skip
|
||||
const uint64_t w = weightp->toUQuad();
|
||||
if (w == 0) continue; // weight=0 means never
|
||||
|
||||
uint64_t effectiveW = w;
|
||||
if (!ditemp->isWhole()) { // := weight is per-value; multiply by range size
|
||||
if (const AstInsideRange* const irp
|
||||
= VN_CAST(ditemp->rangep(), InsideRange)) {
|
||||
const AstConst* const lop = VN_CAST(irp->lhsp(), Const);
|
||||
const AstConst* const hip = VN_CAST(irp->rhsp(), Const);
|
||||
if (lop && hip && hip->toUQuad() >= lop->toUQuad())
|
||||
effectiveW = w * (hip->toUQuad() - lop->toUQuad() + 1);
|
||||
}
|
||||
// scalar: effectiveW stays w
|
||||
}
|
||||
// :/ weight: effectiveW stays w (weight for whole range, not per-value)
|
||||
|
||||
buckets.push_back({ditemp->rangep(), effectiveW});
|
||||
totalWeight += effectiveW;
|
||||
}
|
||||
|
||||
if (buckets.empty() || totalWeight == 0) continue;
|
||||
|
||||
// Create bucket selection variable: uint64_t __Vdist_bucketN
|
||||
const std::string bucketName = "__Vdist_bucket" + cvtToStr(m_distNum++);
|
||||
AstVar* const bucketVarp
|
||||
= new AstVar{fl, VVarType::BLOCKTEMP, bucketName, taskp->findUInt64DType()};
|
||||
bucketVarp->noSubst(true);
|
||||
bucketVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
bucketVarp->funcLocal(true);
|
||||
taskp->addStmtsp(bucketVarp);
|
||||
|
||||
// Assign: bucketVar = (rand64() % totalWeight) + 1
|
||||
// +1 ensures that weight=0 items are never selected
|
||||
AstNodeExpr* randp = new AstRand{fl, nullptr, false};
|
||||
randp->dtypeSetUInt64();
|
||||
taskp->addStmtsp(new AstAssign{
|
||||
fl, new AstVarRef{fl, bucketVarp, VAccess::WRITE},
|
||||
new AstAdd{
|
||||
fl, new AstConst{fl, AstConst::Unsized64{}, 1},
|
||||
new AstModDiv{fl, randp,
|
||||
new AstConst{fl, AstConst::Unsized64{}, totalWeight}}}});
|
||||
|
||||
// Build AstConstraintIf chain (last-to-first so nesting is natural):
|
||||
// if (bucket <= w0) { exprp == range0 }
|
||||
// else if (bucket <= w0+w1) { exprp in range1 }
|
||||
// ...
|
||||
AstNode* chainp = nullptr;
|
||||
uint64_t cumWeight = totalWeight;
|
||||
|
||||
for (int i = static_cast<int>(buckets.size()) - 1; i >= 0; --i) {
|
||||
cumWeight -= buckets[i].effectiveWeight;
|
||||
const uint64_t thisCumWeight = cumWeight + buckets[i].effectiveWeight;
|
||||
|
||||
// Build range constraint expression (user1=true for rand-dependent nodes)
|
||||
AstNodeExpr* constraintExprp;
|
||||
if (const AstInsideRange* const irp = VN_CAST(buckets[i].rangep, InsideRange)) {
|
||||
// Range bucket: exprp >= lo && exprp <= hi
|
||||
AstNodeExpr* const exprCopy1p = distp->exprp()->cloneTreePure(false);
|
||||
exprCopy1p->user1(true);
|
||||
AstNodeExpr* const exprCopy2p = distp->exprp()->cloneTreePure(false);
|
||||
exprCopy2p->user1(true);
|
||||
AstGte* const gtep
|
||||
= new AstGte{fl, exprCopy1p, irp->lhsp()->cloneTreePure(false)};
|
||||
gtep->user1(true);
|
||||
AstLte* const ltep
|
||||
= new AstLte{fl, exprCopy2p, irp->rhsp()->cloneTreePure(false)};
|
||||
ltep->user1(true);
|
||||
constraintExprp = new AstLogAnd{fl, gtep, ltep};
|
||||
constraintExprp->user1(true);
|
||||
} else {
|
||||
// Scalar bucket: exprp == value
|
||||
AstNodeExpr* const exprCopyp = distp->exprp()->cloneTreePure(false);
|
||||
exprCopyp->user1(true);
|
||||
constraintExprp = new AstEq{fl, exprCopyp,
|
||||
buckets[i].rangep->cloneTreePure(false)};
|
||||
constraintExprp->user1(true);
|
||||
}
|
||||
|
||||
AstConstraintExpr* const thenp = new AstConstraintExpr{fl, constraintExprp};
|
||||
|
||||
if (!chainp) {
|
||||
// Last bucket: no condition needed (always selected when reached)
|
||||
chainp = thenp;
|
||||
} else {
|
||||
// Bucket condition: bucketVar <= thisCumWeight (NOT rand-dependent)
|
||||
AstNodeExpr* const condp = new AstLte{
|
||||
fl, new AstVarRef{fl, bucketVarp, VAccess::READ},
|
||||
new AstConst{fl, AstConst::Unsized64{}, thisCumWeight}};
|
||||
chainp = new AstConstraintIf{fl, condp, thenp, chainp};
|
||||
}
|
||||
}
|
||||
|
||||
if (chainp) {
|
||||
// Replace AstConstraintExpr(AstDist) with the weighted-bucket chain
|
||||
constrExprp->replaceWith(chainp);
|
||||
VL_DO_DANGLING(constrExprp->deleteTree(), constrExprp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
|
|
@ -3346,6 +3473,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_randCaseNum);
|
||||
m_modp = nodep;
|
||||
m_randCaseNum = 0;
|
||||
m_distNum = 0;
|
||||
m_writtenVars.clear(); // Each class has its own set of written variables
|
||||
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -3393,6 +3521,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
if (constrp->itemsp()) expandUniqueElementList(constrp->itemsp());
|
||||
// Lower dist constraints into weighted bucket selection before SMT encoding
|
||||
if (constrp->itemsp()) lowerDistConstraints(taskp, constrp->itemsp());
|
||||
ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), nullptr,
|
||||
genp, randModeVarp, m_writtenVars};
|
||||
if (constrp->itemsp()) {
|
||||
|
|
|
|||
|
|
@ -3050,7 +3050,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstDist* nodep) override {
|
||||
// x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b)
|
||||
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
|
||||
// (only outside constraints; inside constraints V3Randomize handles weighted selection)
|
||||
userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
|
||||
for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
|
||||
nextip = itemp->nextp(); // iterate may cause the node to get replaced
|
||||
|
|
@ -3087,6 +3087,12 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (!VN_IS(itemp, InsideRange))
|
||||
iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
|
||||
}
|
||||
|
||||
// Inside a constraint: keep AstDist alive for V3Randomize to do weighted bucket selection
|
||||
if (m_constraintp) return;
|
||||
|
||||
// Outside constraint: lower to inside expressions (ignores weights - imperfect)
|
||||
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
|
||||
AstNodeExpr* newp = nullptr;
|
||||
for (AstDistItem* itemp = nodep->itemsp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), DistItem)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue