Skip to content

Commit bdb7e33

Browse files
committed
Move from Expressions to AssertionExpr.
1 parent c9e04ef commit bdb7e33

2 files changed

Lines changed: 157 additions & 121 deletions

File tree

lib/Conversion/ImportVerilog/AssertionExpr.cpp

Lines changed: 1 addition & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99
#include "slang/ast/expressions/AssertionExpr.h"
1010

1111
#include "ImportVerilogInternals.h"
12-
#include "circt/Dialect/Comb/CombDialect.h"
1312
#include "circt/Dialect/Comb/CombOps.h"
1413
#include "circt/Dialect/LTL/LTLOps.h"
1514
#include "circt/Dialect/Moore/MooreOps.h"
16-
#include "circt/Support/FVInt.h"
1715
#include "circt/Support/LLVM.h"
1816
#include "mlir/IR/BuiltinAttributes.h"
1917
#include "mlir/Support/LLVM.h"
@@ -362,71 +360,11 @@ FailureOr<Value> Context::convertAssertionSystemCallArity1(
362360
case ksn::Past:
363361
return castToMoore(ltl::PastOp::create(builder, loc, value, 1, clockVal));
364362

365-
case ksn::OneHot0: {
366-
auto one = hw::ConstantOp::create(builder, loc, value.getType(), 1);
367-
auto minusOne = comb::SubOp::create(builder, loc, value, one);
368-
auto anded = comb::AndOp::create(builder, loc, value, minusOne);
369-
auto zero = hw::ConstantOp::create(builder, loc, value.getType(), 0);
370-
return castToTwoValued(comb::ICmpOp::create(
371-
builder, loc, comb::ICmpPredicate::eq, anded, zero, false));
372-
}
373-
374-
case ksn::OneHot: {
375-
auto one = hw::ConstantOp::create(builder, loc, value.getType(), 1);
376-
auto minusOne = comb::SubOp::create(builder, loc, value, one);
377-
auto anded = comb::AndOp::create(builder, loc, value, minusOne);
378-
auto zero = hw::ConstantOp::create(builder, loc, value.getType(), 0);
379-
auto isOneHot0 = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::eq,
380-
anded, zero, false);
381-
auto isNotZero = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::ne,
382-
value, zero, false);
383-
auto result = comb::AndOp::create(builder, loc, isOneHot0, isNotZero);
384-
return castToTwoValued(result);
385-
}
386-
387-
case ksn::CountOnes: {
388-
// Popcount: extract each bit, zero-extend to result width, and sum.
389-
auto intTy = cast<IntegerType>(value.getType());
390-
unsigned width = intTy.getWidth();
391-
unsigned resultWidth = llvm::Log2_32_Ceil(width + 1);
392-
auto i1Ty = builder.getI1Type();
393-
unsigned padWidth = resultWidth - 1;
394-
auto zeros = hw::ConstantOp::create(builder, loc,
395-
builder.getIntegerType(padWidth), 0);
396-
397-
// Zero-extend the first bit to seed the accumulator.
398-
auto bit0 = comb::ExtractOp::create(builder, loc, i1Ty, value, 0);
399-
Value sum = comb::ConcatOp::create(builder, loc, ValueRange{zeros, bit0});
400-
401-
for (unsigned i = 1; i < width; ++i) {
402-
auto bit = comb::ExtractOp::create(builder, loc, i1Ty, value, i);
403-
auto extended =
404-
comb::ConcatOp::create(builder, loc, ValueRange{zeros, bit});
405-
sum = comb::AddOp::create(builder, loc, sum, extended);
406-
}
407-
return castToTwoValued(sum);
408-
}
409-
410363
default:
411364
return Value{};
412365
}
413366
}
414367

415-
static Value getIsUnknown(OpBuilder &builder, Location loc, Value value,
416-
moore::IntType valTy, MLIRContext *ctx) {
417-
Value bitVal = value;
418-
if (valTy.getWidth() > 1) {
419-
auto mooreI1Type = moore::IntType::get(ctx, 1, valTy.getDomain());
420-
bitVal = moore::ReduceXorOp::create(builder, loc, mooreI1Type, value);
421-
}
422-
423-
auto xType = moore::IntType::get(ctx, 1, moore::Domain::FourValued);
424-
auto xValue = FVInt::getAllX(1);
425-
auto xConst = moore::ConstantOp::create(builder, loc, xType, xValue);
426-
427-
return moore::CaseEqOp::create(builder, loc, bitVal, xConst).getResult();
428-
}
429-
430368
Value Context::convertAssertionCallExpression(
431369
const slang::ast::CallExpression &expr,
432370
const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
@@ -482,54 +420,6 @@ Value Context::convertAssertionCallExpression(
482420
return {};
483421
}
484422

485-
// IsUnknown is handled here rather than below with the others as below it
486-
// would have already been converted to an integer type rather than bool
487-
// type as here.
488-
if (subroutine.knownNameId == slang::parsing::KnownSystemName::IsUnknown) {
489-
result = getIsUnknown(builder, loc, value, valTy, getContext());
490-
break;
491-
}
492-
493-
// OneHot/OneHot0 are handled both here and below.
494-
if ((subroutine.knownNameId == slang::parsing::KnownSystemName::OneHot ||
495-
subroutine.knownNameId == slang::parsing::KnownSystemName::OneHot0) &&
496-
(valTy.getDomain() == Domain::FourValued)) {
497-
// In SystemVerilog, these system only returns 1b1 if the expression is
498-
// fully known and the condition is met. So if any x or y bits, then
499-
// these must return 1'b0.
500-
501-
// Detect if input contain unknown bits.
502-
Value isUnknownMoore =
503-
getIsUnknown(builder, loc, value, valTy, getContext());
504-
Value isUnknown =
505-
builder.createOrFold<moore::ToBuiltinIntOp>(loc, isUnknownMoore);
506-
507-
Value coerced = builder.createOrFold<moore::LogicToIntOp>(loc, value);
508-
Value state2IntVal =
509-
builder.createOrFold<moore::ToBuiltinIntOp>(loc, coerced);
510-
if (!state2IntVal)
511-
return {};
512-
513-
auto systemResult = this->convertAssertionSystemCallArity1(
514-
subroutine, loc, state2IntVal, originalType, clockVal);
515-
if (failed(systemResult) || !*systemResult)
516-
return {};
517-
Value onehot2state = *systemResult;
518-
519-
// Squash to 0 if unknown bits exist.
520-
Value onehot2stateBuiltin = convertToI1(onehot2state);
521-
Value zeroBuiltin =
522-
hw::ConstantOp::create(builder, loc, builder.getI1Type(), 0);
523-
Value resultBuiltin = comb::MuxOp::create(
524-
builder, loc, isUnknown, zeroBuiltin, onehot2stateBuiltin);
525-
526-
Value finalResult =
527-
moore::FromBuiltinIntOp::create(builder, loc, resultBuiltin);
528-
result =
529-
moore::IntToLogicOp::create(builder, loc, finalResult).getResult();
530-
break;
531-
}
532-
533423
// If the value is four-valued, we need to map it to two-valued before we
534424
// cast it to a builtin int
535425
if (valTy.getDomain() == Domain::FourValued) {
@@ -550,10 +440,7 @@ Value Context::convertAssertionCallExpression(
550440
return {};
551441
if (*result) {
552442
auto expectedTy = convertType(*expr.type);
553-
return materializeConversion(expectedTy, *result,
554-
(subroutine.knownNameId !=
555-
slang::parsing::KnownSystemName::CountOnes) &&
556-
expr.type->isSigned(),
443+
return materializeConversion(expectedTy, *result, expr.type->isSigned(),
557444
loc);
558445
}
559446

lib/Conversion/ImportVerilog/Expressions.cpp

Lines changed: 156 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
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

Comments
 (0)