9#include "slang/ast/expressions/AssertionExpr.h"
16#include "mlir/IR/BuiltinAttributes.h"
17#include "mlir/Support/LLVM.h"
18#include "slang/ast/SystemSubroutine.h"
24using namespace ImportVerilog;
28struct AssertionExprVisitor {
33 AssertionExprVisitor(
Context &context, Location loc)
34 : context(context), loc(loc), builder(context.builder) {}
37 std::pair<mlir::IntegerAttr, mlir::IntegerAttr>
38 convertRangeToAttrs(uint32_t min,
39 std::optional<uint32_t> max = std::nullopt) {
40 auto minAttr = builder.getI64IntegerAttr(min);
41 mlir::IntegerAttr rangeAttr;
42 if (max.has_value()) {
43 rangeAttr = builder.getI64IntegerAttr(max.value() - min);
45 return {minAttr, rangeAttr};
49 Value createRepetition(Location loc,
50 const slang::ast::SequenceRepetition &repetition,
51 Value &inputSequence) {
53 auto [minRepetitions, repetitionRange] =
54 convertRangeToAttrs(repetition.range.min, repetition.range.max);
56 using slang::ast::SequenceRepetition;
59 if ((repetition.kind == SequenceRepetition::Nonconsecutive ||
60 repetition.kind == SequenceRepetition::GoTo) &&
63 repetition.kind == SequenceRepetition::Nonconsecutive
64 ?
"Nonconsecutive repetition requires a maximum value"
65 :
"GoTo repetition requires a maximum value");
69 switch (repetition.kind) {
70 case SequenceRepetition::Consecutive:
71 return ltl::RepeatOp::create(builder, loc, inputSequence, minRepetitions,
73 case SequenceRepetition::Nonconsecutive:
74 return ltl::NonConsecutiveRepeatOp::create(
75 builder, loc, inputSequence, minRepetitions, repetitionRange);
76 case SequenceRepetition::GoTo:
77 return ltl::GoToRepeatOp::create(builder, loc, inputSequence,
78 minRepetitions, repetitionRange);
80 llvm_unreachable(
"All enum values handled in switch");
83 Value visit(
const slang::ast::SimpleAssertionExpr &expr) {
89 auto valueType = value.getType();
92 if (!mlir::isa<ltl::SequenceType, ltl::PropertyType, mlir::IntegerType>(
101 if (!expr.repetition.has_value()) {
107 return createRepetition(loc, expr.repetition.value(), value);
110 Value visit(
const slang::ast::SequenceConcatExpr &expr) {
112 assert(!expr.elements.empty());
114 SmallVector<Value> sequenceElements;
116 for (
const auto &concatElement : expr.elements) {
117 Value sequenceValue =
122 [[maybe_unused]] Type valueType = sequenceValue.getType();
123 assert(valueType.isInteger(1) || mlir::isa<ltl::SequenceType>(valueType));
125 auto [delayMin, delayRange] =
126 convertRangeToAttrs(concatElement.delay.min, concatElement.delay.max);
127 auto delayedSequence = ltl::DelayOp::create(builder, loc, sequenceValue,
128 delayMin, delayRange);
129 sequenceElements.push_back(delayedSequence);
132 return builder.createOrFold<ltl::ConcatOp>(loc, sequenceElements);
135 Value visit(
const slang::ast::UnaryAssertionExpr &expr) {
139 using slang::ast::UnaryAssertionOperator;
141 case UnaryAssertionOperator::Not:
142 return ltl::NotOp::create(builder, loc, value);
143 case UnaryAssertionOperator::SEventually:
144 if (expr.range.has_value()) {
145 mlir::emitError(loc,
"Strong eventually with range not supported");
148 return ltl::EventuallyOp::create(builder, loc, value);
150 case UnaryAssertionOperator::Always: {
151 std::pair<mlir::IntegerAttr, mlir::IntegerAttr> attr = {
152 builder.getI64IntegerAttr(0), mlir::IntegerAttr{}};
153 if (expr.range.has_value()) {
155 convertRangeToAttrs(expr.range.value().min, expr.range.value().max);
157 return ltl::RepeatOp::create(builder, loc, value, attr.first,
160 case UnaryAssertionOperator::NextTime: {
161 auto minRepetitions = builder.getI64IntegerAttr(1);
162 if (expr.range.has_value()) {
163 minRepetitions = builder.getI64IntegerAttr(expr.range.value().min);
165 return ltl::DelayOp::create(builder, loc, value, minRepetitions,
166 builder.getI64IntegerAttr(0));
168 case UnaryAssertionOperator::Eventually:
169 case UnaryAssertionOperator::SNextTime:
170 case UnaryAssertionOperator::SAlways:
171 mlir::emitError(loc,
"unsupported unary operator: ")
172 << slang::ast::toString(expr.op);
175 llvm_unreachable(
"All enum values handled in switch");
178 Value visit(
const slang::ast::BinaryAssertionExpr &expr) {
183 SmallVector<Value, 2> operands = {lhs, rhs};
184 using slang::ast::BinaryAssertionOperator;
186 case BinaryAssertionOperator::And:
187 return ltl::AndOp::create(builder, loc, operands);
188 case BinaryAssertionOperator::Or:
189 return ltl::OrOp::create(builder, loc, operands);
190 case BinaryAssertionOperator::Intersect:
191 return ltl::IntersectOp::create(builder, loc, operands);
192 case BinaryAssertionOperator::Throughout: {
193 auto lhsRepeat = ltl::RepeatOp::create(
194 builder, loc, lhs, builder.getI64IntegerAttr(0), mlir::IntegerAttr{});
195 return ltl::IntersectOp::create(builder, loc,
196 SmallVector<Value, 2>{lhsRepeat, rhs});
198 case BinaryAssertionOperator::Within: {
201 auto oneRepeat = ltl::RepeatOp::create(builder, loc, constOne,
202 builder.getI64IntegerAttr(0),
203 mlir::IntegerAttr{});
204 auto repeatDelay = ltl::DelayOp::create(builder, loc, oneRepeat,
205 builder.getI64IntegerAttr(1),
206 builder.getI64IntegerAttr(0));
208 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
209 builder.getI64IntegerAttr(0));
210 auto combined = ltl::ConcatOp::create(
211 builder, loc, SmallVector<Value, 3>{repeatDelay, lhsDelay, constOne});
212 return ltl::IntersectOp::create(builder, loc,
213 SmallVector<Value, 2>{combined, rhs});
215 case BinaryAssertionOperator::Iff: {
216 auto ored = ltl::OrOp::create(builder, loc, operands);
217 auto notOred = ltl::NotOp::create(builder, loc, ored);
218 auto anded = ltl::AndOp::create(builder, loc, operands);
219 return ltl::OrOp::create(builder, loc,
220 SmallVector<Value, 2>{notOred, anded});
222 case BinaryAssertionOperator::Until:
223 return ltl::UntilOp::create(builder, loc, operands);
224 case BinaryAssertionOperator::UntilWith: {
225 auto untilOp = ltl::UntilOp::create(builder, loc, operands);
226 auto andOp = ltl::AndOp::create(builder, loc, operands);
227 auto notUntil = ltl::NotOp::create(builder, loc, untilOp);
228 return ltl::OrOp::create(builder, loc,
229 SmallVector<Value, 2>{notUntil, andOp});
231 case BinaryAssertionOperator::Implies: {
232 auto notLhs = ltl::NotOp::create(builder, loc, lhs);
233 return ltl::OrOp::create(builder, loc,
234 SmallVector<Value, 2>{notLhs, rhs});
236 case BinaryAssertionOperator::OverlappedImplication:
237 return ltl::ImplicationOp::create(builder, loc, operands);
238 case BinaryAssertionOperator::NonOverlappedImplication: {
242 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
243 builder.getI64IntegerAttr(0));
244 auto antecedent = ltl::ConcatOp::create(
245 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
246 return ltl::ImplicationOp::create(builder, loc,
247 SmallVector<Value, 2>{antecedent, rhs});
249 case BinaryAssertionOperator::OverlappedFollowedBy: {
250 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
251 auto implication = ltl::ImplicationOp::create(
252 builder, loc, SmallVector<Value, 2>{lhs, notRhs});
253 return ltl::NotOp::create(builder, loc, implication);
255 case BinaryAssertionOperator::NonOverlappedFollowedBy: {
258 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
260 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
261 builder.getI64IntegerAttr(0));
262 auto antecedent = ltl::ConcatOp::create(
263 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
264 auto implication = ltl::ImplicationOp::create(
265 builder, loc, SmallVector<Value, 2>{antecedent, notRhs});
266 return ltl::NotOp::create(builder, loc, implication);
268 case BinaryAssertionOperator::SUntil:
269 case BinaryAssertionOperator::SUntilWith:
270 mlir::emitError(loc,
"unsupported binary operator: ")
271 << slang::ast::toString(expr.op);
274 llvm_unreachable(
"All enum values handled in switch");
277 Value visit(
const slang::ast::ClockingAssertionExpr &expr) {
285 template <
typename T>
286 Value visit(T &&node) {
287 mlir::emitError(loc,
"unsupported expression: ")
288 << slang::ast::toString(node.kind);
292 Value visitInvalid(
const slang::ast::AssertionExpr &expr) {
293 mlir::emitError(loc,
"invalid expression");
299FailureOr<Value> Context::convertAssertionSystemCallArity1(
300 const slang::ast::SystemSubroutine &subroutine, Location loc, Value value,
304 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
307 Value sampled = ltl::SampledOp::create(
builder, loc, value);
310 if (
auto ty = dyn_cast<moore::IntType>(originalType)) {
312 moore::FromBuiltinIntOp::create(
builder, loc, sampled);
313 if (ty.getDomain() == Domain::FourValued)
315 moore::IntToLogicOp::create(
builder, loc, sampled);
322 auto current = value;
324 ltl::PastOp::create(
builder, loc, value, 1, Value{})
326 auto fell = comb::ICmpOp::create(
builder, loc,
327 comb::ICmpPredicate::ugt,
328 past, current,
false)
336 ltl::PastOp::create(
builder, loc, value, 1, Value{})
338 auto current = value;
339 auto rose = comb::ICmpOp::create(
builder, loc,
340 comb::ICmpPredicate::ult,
341 past, current,
false)
349 ltl::PastOp::create(
builder, loc, value, 1, Value{})
351 auto current = value;
352 auto changed = comb::ICmpOp::create(
builder, loc,
353 comb::ICmpPredicate::ne,
354 past, current,
false)
362 ltl::PastOp::create(
builder, loc, value, 1, Value{})
364 auto current = value;
365 auto stable = comb::ICmpOp::create(
builder, loc,
366 comb::ICmpPredicate::eq,
367 past, current,
false)
374 ltl::PastOp::create(
builder, loc, value, 1, Value{});
377 if (
auto ty = dyn_cast<moore::IntType>(originalType)) {
378 past = moore::FromBuiltinIntOp::create(
builder, loc, past);
379 if (ty.getDomain() == Domain::FourValued)
380 past = moore::IntToLogicOp::create(
builder, loc, past);
384 .Default([&]() -> Value {
return {}; });
385 return systemCallRes();
389 const slang::ast::CallExpression &expr,
390 const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
392 const auto &subroutine = *info.subroutine;
393 auto args = expr.arguments();
395 FailureOr<Value> result;
399 moore::IntType valTy;
401 switch (args.size()) {
404 originalType = value.getType();
405 valTy = dyn_cast<moore::IntType>(value.getType());
407 mlir::emitError(loc) <<
"expected integer argument for system call `"
408 << subroutine.name <<
"`";
413 if (valTy.getDomain() == Domain::FourValued) {
414 value =
builder.createOrFold<moore::LogicToIntOp>(loc, value);
416 intVal =
builder.createOrFold<moore::ToBuiltinIntOp>(loc, value);
432 mlir::emitError(loc) <<
"unsupported system call `" << subroutine.name <<
"`";
438 AssertionExprVisitor visitor{*
this, loc};
439 return expr.visit(visitor);
447 auto loc = value.getLoc();
448 auto type = dyn_cast<moore::IntType>(value.getType());
449 if (!type || type.getBitSize() != 1) {
450 mlir::emitError(loc,
"expected a 1-bit integer");
453 if (type.getDomain() == Domain::FourValued) {
454 value = moore::LogicToIntOp::create(
builder, loc, value);
456 return moore::ToBuiltinIntOp::create(
builder, loc, value);
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
A helper class to facilitate the conversion from a Slang AST to MLIR operations.
Value convertToI1(Value value)
Helper function to convert a value to a MLIR I1 value.
Value convertLTLTimingControl(const slang::ast::TimingControl &ctrl, const Value &seqOrPro)
OpBuilder builder
The builder used to create IR operations.
Value convertAssertionCallExpression(const slang::ast::CallExpression &expr, const slang::ast::CallExpression::SystemCallInfo &info, Location loc)
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value convertAssertionExpression(const slang::ast::AssertionExpr &expr, Location loc)
FailureOr< Value > convertAssertionSystemCallArity1(const slang::ast::SystemSubroutine &subroutine, Location loc, Value value, Type originalType)
Convert system function calls within properties and assertion with a single argument.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.