77// ===----------------------------------------------------------------------===//
88
99#include " ImportVerilogInternals.h"
10+ #include " circt/Dialect/Comb/CombOps.h"
1011#include " circt/Dialect/Moore/MooreOps.h"
1112#include " circt/Dialect/Moore/MooreTypes.h"
13+ #include " circt/Support/FVInt.h"
1214#include " mlir/IR/Operation.h"
1315#include " mlir/IR/Value.h"
1416#include " slang/ast/EvalContext.h"
@@ -2031,7 +2033,7 @@ struct RvalueExprVisitor : public ExprVisitor {
20312033 const auto &subroutine = *info.subroutine ;
20322034 auto nameId = subroutine.knownNameId ;
20332035
2034- // $rose, $fell, $stable, $changed, and $past are only valid in
2036+ // $rose, $fell, $stable, $changed, $past, and $sampled are only valid in
20352037 // the context of properties and assertions. Those are treated in the
20362038 // LTLDialect; treat them there instead.
20372039 switch (nameId) {
@@ -2041,10 +2043,6 @@ struct RvalueExprVisitor : public ExprVisitor {
20412043 case ksn::Changed:
20422044 case ksn::Past:
20432045 case ksn::Sampled:
2044- case ksn::IsUnknown:
2045- case ksn::OneHot:
2046- case ksn::OneHot0:
2047- case ksn::CountOnes:
20482046 return context.convertAssertionCallExpression (expr, info, loc);
20492047 default :
20502048 break ;
@@ -2073,8 +2071,14 @@ struct RvalueExprVisitor : public ExprVisitor {
20732071 return {};
20742072
20752073 auto ty = context.convertType (*expr.type );
2076- return context.materializeConversion (ty, result, expr.type ->isSigned (),
2077- loc);
2074+ // Bit vector builtins ($countones, $isunknown, $onehot, $onehot0) return
2075+ // inherently unsigned results that must be zero-extended, even though
2076+ // Slang's declared return type may be signed int.
2077+ bool isSigned = expr.type ->isSigned ();
2078+ if (nameId == ksn::CountOnes || nameId == ksn::IsUnknown ||
2079+ nameId == ksn::OneHot || nameId == ksn::OneHot0)
2080+ isSigned = false ;
2081+ return context.materializeConversion (ty, result, isSigned, loc);
20782082 }
20792083
20802084 // / Handle string literals.
@@ -3263,6 +3267,151 @@ Value Context::convertSystemCall(
32633267 return moore::Clog2BIOp::create (builder, loc, value);
32643268 }
32653269
3270+ // ===--------------------------------------------------------------------===//
3271+ // Bit Vector System Functions
3272+ // ===--------------------------------------------------------------------===//
3273+
3274+ if (nameId == ksn::IsUnknown) {
3275+ assert (numArgs == 1 && " `$isunknown` takes 1 argument" );
3276+ auto value = convertRvalueExpression (*args[0 ]);
3277+ if (!value)
3278+ return {};
3279+ auto valTy = dyn_cast<moore::IntType>(value.getType ());
3280+ if (!valTy) {
3281+ mlir::emitError (loc) << " expected integer argument for `$isunknown`" ;
3282+ return {};
3283+ }
3284+ Value bitVal = value;
3285+ if (valTy.getWidth () > 1 ) {
3286+ auto mooreI1Type =
3287+ moore::IntType::get (getContext (), 1 , valTy.getDomain ());
3288+ bitVal = moore::ReduceXorOp::create (builder, loc, mooreI1Type, value);
3289+ }
3290+ auto xType =
3291+ moore::IntType::get (getContext (), 1 , moore::Domain::FourValued);
3292+ auto xValue = FVInt::getAllX (1 );
3293+ auto xConst = moore::ConstantOp::create (builder, loc, xType, xValue);
3294+ return moore::CaseEqOp::create (builder, loc, bitVal, xConst).getResult ();
3295+ }
3296+
3297+ if (nameId == ksn::OneHot0 || nameId == ksn::OneHot) {
3298+ assert (numArgs == 1 && " `$onehot`/`$onehot0` takes 1 argument" );
3299+ auto value = convertRvalueExpression (*args[0 ]);
3300+ if (!value)
3301+ return {};
3302+ auto valTy = dyn_cast<moore::IntType>(value.getType ());
3303+ if (!valTy) {
3304+ mlir::emitError (loc) << " expected integer argument for `"
3305+ << subroutine.name << " `" ;
3306+ return {};
3307+ }
3308+
3309+ // In SystemVerilog, $onehot/$onehot0 return 1'b0 if the expression
3310+ // contains any unknown (x/z) bits. Detect and squash if four-valued.
3311+ Value isUnknown;
3312+ if (valTy.getDomain () == Domain::FourValued) {
3313+ Value bitVal = value;
3314+ if (valTy.getWidth () > 1 ) {
3315+ auto mooreI1Type =
3316+ moore::IntType::get (getContext (), 1 , valTy.getDomain ());
3317+ bitVal = moore::ReduceXorOp::create (builder, loc, mooreI1Type, value);
3318+ }
3319+ auto xType =
3320+ moore::IntType::get (getContext (), 1 , moore::Domain::FourValued);
3321+ auto xConst =
3322+ moore::ConstantOp::create (builder, loc, xType, FVInt::getAllX (1 ));
3323+ Value isUnknownMoore =
3324+ moore::CaseEqOp::create (builder, loc, bitVal, xConst).getResult ();
3325+ isUnknown =
3326+ builder.createOrFold <moore::ToBuiltinIntOp>(loc, isUnknownMoore);
3327+ }
3328+
3329+ // Coerce four-valued input to two-valued for the comb ops.
3330+ Value intVal;
3331+ if (valTy.getDomain () == Domain::FourValued) {
3332+ Value coerced = builder.createOrFold <moore::LogicToIntOp>(loc, value);
3333+ intVal = builder.createOrFold <moore::ToBuiltinIntOp>(loc, coerced);
3334+ } else {
3335+ intVal = builder.createOrFold <moore::ToBuiltinIntOp>(loc, value);
3336+ }
3337+
3338+ // Compute onehot0: (value & (value - 1)) == 0
3339+ auto one = hw::ConstantOp::create (builder, loc, intVal.getType (), 1 );
3340+ auto minusOne = comb::SubOp::create (builder, loc, intVal, one);
3341+ auto anded = comb::AndOp::create (builder, loc, intVal, minusOne);
3342+ auto zero = hw::ConstantOp::create (builder, loc, intVal.getType (), 0 );
3343+ Value result = comb::ICmpOp::create (builder, loc, comb::ICmpPredicate::eq,
3344+ anded, zero, false );
3345+
3346+ // For $onehot, additionally require value != 0.
3347+ if (nameId == ksn::OneHot) {
3348+ auto isNotZero = comb::ICmpOp::create (
3349+ builder, loc, comb::ICmpPredicate::ne, intVal, zero, false );
3350+ result = comb::AndOp::create (builder, loc, result, isNotZero);
3351+ }
3352+
3353+ // Wrap back into Moore type.
3354+ result = moore::FromBuiltinIntOp::create (builder, loc, result);
3355+
3356+ // If four-valued, squash to 0 when unknown bits exist.
3357+ if (isUnknown) {
3358+ Value resultI1 = convertToI1 (result);
3359+ Value zeroI1 =
3360+ hw::ConstantOp::create (builder, loc, builder.getI1Type (), 0 );
3361+ Value squashed =
3362+ comb::MuxOp::create (builder, loc, isUnknown, zeroI1, resultI1);
3363+ Value squashedMoore =
3364+ moore::FromBuiltinIntOp::create (builder, loc, squashed);
3365+ return moore::IntToLogicOp::create (builder, loc, squashedMoore)
3366+ .getResult ();
3367+ }
3368+ return result;
3369+ }
3370+
3371+ if (nameId == ksn::CountOnes) {
3372+ assert (numArgs == 1 && " `$countones` takes 1 argument" );
3373+ auto value = convertRvalueExpression (*args[0 ]);
3374+ if (!value)
3375+ return {};
3376+ auto valTy = dyn_cast<moore::IntType>(value.getType ());
3377+ if (!valTy) {
3378+ mlir::emitError (loc) << " expected integer argument for `$countones`" ;
3379+ return {};
3380+ }
3381+
3382+ // Coerce four-valued input to two-valued for the comb ops.
3383+ Value intVal;
3384+ if (valTy.getDomain () == Domain::FourValued) {
3385+ Value coerced = builder.createOrFold <moore::LogicToIntOp>(loc, value);
3386+ intVal = builder.createOrFold <moore::ToBuiltinIntOp>(loc, coerced);
3387+ } else {
3388+ intVal = builder.createOrFold <moore::ToBuiltinIntOp>(loc, value);
3389+ }
3390+
3391+ // Popcount: extract each bit, zero-extend to result width, and sum.
3392+ auto builtinIntTy = cast<IntegerType>(intVal.getType ());
3393+ unsigned width = builtinIntTy.getWidth ();
3394+ unsigned resultWidth = llvm::Log2_32_Ceil (width + 1 );
3395+ auto i1Ty = builder.getI1Type ();
3396+ unsigned padWidth = resultWidth - 1 ;
3397+ auto zeros = hw::ConstantOp::create (builder, loc,
3398+ builder.getIntegerType (padWidth), 0 );
3399+
3400+ // Zero-extend the first bit to seed the accumulator.
3401+ auto bit0 = comb::ExtractOp::create (builder, loc, i1Ty, intVal, 0 );
3402+ Value sum = comb::ConcatOp::create (builder, loc, ValueRange{zeros, bit0});
3403+
3404+ for (unsigned i = 1 ; i < width; ++i) {
3405+ auto bit = comb::ExtractOp::create (builder, loc, i1Ty, intVal, i);
3406+ auto extended =
3407+ comb::ConcatOp::create (builder, loc, ValueRange{zeros, bit});
3408+ sum = comb::AddOp::create (builder, loc, sum, extended);
3409+ }
3410+
3411+ // Wrap back into Moore type (unsigned — CountOnes result is never signed).
3412+ return moore::FromBuiltinIntOp::create (builder, loc, sum);
3413+ }
3414+
32663415 // Real math functions (all take 1 real argument)
32673416 if (nameId == ksn::Ln)
32683417 return convertRealMathBI<moore::LnBIOp>(*this , loc, name, args);
0 commit comments