9#include "slang/ast/expressions/AssertionExpr.h"
15#include "mlir/IR/BuiltinAttributes.h"
16#include "mlir/Support/LLVM.h"
17#include "slang/ast/SystemSubroutine.h"
23using namespace ImportVerilog;
27struct AssertionExprVisitor {
32 AssertionExprVisitor(
Context &context, Location loc)
33 : context(context), loc(loc), builder(context.builder) {}
36 std::pair<mlir::IntegerAttr, mlir::IntegerAttr>
37 convertRangeToAttrs(uint32_t min,
38 std::optional<uint32_t> max = std::nullopt) {
39 auto minAttr = builder.getI64IntegerAttr(min);
40 mlir::IntegerAttr rangeAttr;
41 if (max.has_value()) {
42 rangeAttr = builder.getI64IntegerAttr(max.value() - min);
44 return {minAttr, rangeAttr};
48 Value createRepetition(Location loc,
49 const slang::ast::SequenceRepetition &repetition,
50 Value &inputSequence) {
52 auto [minRepetitions, repetitionRange] =
53 convertRangeToAttrs(repetition.range.min, repetition.range.max);
55 using slang::ast::SequenceRepetition;
58 if ((repetition.kind == SequenceRepetition::Nonconsecutive ||
59 repetition.kind == SequenceRepetition::GoTo) &&
62 repetition.kind == SequenceRepetition::Nonconsecutive
63 ?
"Nonconsecutive repetition requires a maximum value"
64 :
"GoTo repetition requires a maximum value");
68 switch (repetition.kind) {
69 case SequenceRepetition::Consecutive:
70 return ltl::RepeatOp::create(builder, loc, inputSequence, minRepetitions,
72 case SequenceRepetition::Nonconsecutive:
73 return ltl::NonConsecutiveRepeatOp::create(
74 builder, loc, inputSequence, minRepetitions, repetitionRange);
75 case SequenceRepetition::GoTo:
76 return ltl::GoToRepeatOp::create(builder, loc, inputSequence,
77 minRepetitions, repetitionRange);
79 llvm_unreachable(
"All enum values handled in switch");
82 Value visit(
const slang::ast::SimpleAssertionExpr &expr) {
88 auto valueType = value.getType();
91 if (!mlir::isa<ltl::SequenceType, ltl::PropertyType, mlir::IntegerType>(
100 if (!expr.repetition.has_value()) {
106 return createRepetition(loc, expr.repetition.value(), value);
109 Value visit(
const slang::ast::SequenceConcatExpr &expr) {
111 assert(!expr.elements.empty());
113 SmallVector<Value> sequenceElements;
115 for (
const auto &concatElement : expr.elements) {
116 Value sequenceValue =
121 [[maybe_unused]] Type valueType = sequenceValue.getType();
122 assert(valueType.isInteger(1) || mlir::isa<ltl::SequenceType>(valueType));
124 auto [delayMin, delayRange] =
125 convertRangeToAttrs(concatElement.delay.min, concatElement.delay.max);
126 auto delayedSequence = ltl::DelayOp::create(builder, loc, sequenceValue,
127 delayMin, delayRange);
128 sequenceElements.push_back(delayedSequence);
131 return builder.createOrFold<ltl::ConcatOp>(loc, sequenceElements);
134 Value visit(
const slang::ast::UnaryAssertionExpr &expr) {
138 using slang::ast::UnaryAssertionOperator;
140 case UnaryAssertionOperator::Not:
141 return ltl::NotOp::create(builder, loc, value);
142 case UnaryAssertionOperator::SEventually:
143 if (expr.range.has_value()) {
144 mlir::emitError(loc,
"Strong eventually with range not supported");
147 return ltl::EventuallyOp::create(builder, loc, value);
149 case UnaryAssertionOperator::Always: {
150 std::pair<mlir::IntegerAttr, mlir::IntegerAttr> attr = {
151 builder.getI64IntegerAttr(0), mlir::IntegerAttr{}};
152 if (expr.range.has_value()) {
154 convertRangeToAttrs(expr.range.value().min, expr.range.value().max);
156 return ltl::RepeatOp::create(builder, loc, value, attr.first,
159 case UnaryAssertionOperator::NextTime: {
160 auto minRepetitions = builder.getI64IntegerAttr(1);
161 if (expr.range.has_value()) {
162 minRepetitions = builder.getI64IntegerAttr(expr.range.value().min);
164 return ltl::DelayOp::create(builder, loc, value, minRepetitions,
165 builder.getI64IntegerAttr(0));
167 case UnaryAssertionOperator::Eventually:
168 case UnaryAssertionOperator::SNextTime:
169 case UnaryAssertionOperator::SAlways:
170 mlir::emitError(loc,
"unsupported unary operator: ")
171 << slang::ast::toString(expr.op);
174 llvm_unreachable(
"All enum values handled in switch");
177 Value visit(
const slang::ast::BinaryAssertionExpr &expr) {
182 SmallVector<Value, 2> operands = {lhs, rhs};
183 using slang::ast::BinaryAssertionOperator;
185 case BinaryAssertionOperator::And:
186 return ltl::AndOp::create(builder, loc, operands);
187 case BinaryAssertionOperator::Or:
188 return ltl::OrOp::create(builder, loc, operands);
189 case BinaryAssertionOperator::Intersect:
190 return ltl::IntersectOp::create(builder, loc, operands);
191 case BinaryAssertionOperator::Throughout: {
192 auto lhsRepeat = ltl::RepeatOp::create(
193 builder, loc, lhs, builder.getI64IntegerAttr(0), mlir::IntegerAttr{});
194 return ltl::IntersectOp::create(builder, loc,
195 SmallVector<Value, 2>{lhsRepeat, rhs});
197 case BinaryAssertionOperator::Within: {
200 auto oneRepeat = ltl::RepeatOp::create(builder, loc, constOne,
201 builder.getI64IntegerAttr(0),
202 mlir::IntegerAttr{});
203 auto repeatDelay = ltl::DelayOp::create(builder, loc, oneRepeat,
204 builder.getI64IntegerAttr(1),
205 builder.getI64IntegerAttr(0));
207 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
208 builder.getI64IntegerAttr(0));
209 auto combined = ltl::ConcatOp::create(
210 builder, loc, SmallVector<Value, 3>{repeatDelay, lhsDelay, constOne});
211 return ltl::IntersectOp::create(builder, loc,
212 SmallVector<Value, 2>{combined, rhs});
214 case BinaryAssertionOperator::Iff: {
215 auto ored = ltl::OrOp::create(builder, loc, operands);
216 auto notOred = ltl::NotOp::create(builder, loc, ored);
217 auto anded = ltl::AndOp::create(builder, loc, operands);
218 return ltl::OrOp::create(builder, loc,
219 SmallVector<Value, 2>{notOred, anded});
221 case BinaryAssertionOperator::Until:
222 return ltl::UntilOp::create(builder, loc, operands);
223 case BinaryAssertionOperator::UntilWith: {
224 auto untilOp = ltl::UntilOp::create(builder, loc, operands);
225 auto andOp = ltl::AndOp::create(builder, loc, operands);
226 auto notUntil = ltl::NotOp::create(builder, loc, untilOp);
227 return ltl::OrOp::create(builder, loc,
228 SmallVector<Value, 2>{notUntil, andOp});
230 case BinaryAssertionOperator::Implies: {
231 auto notLhs = ltl::NotOp::create(builder, loc, lhs);
232 return ltl::OrOp::create(builder, loc,
233 SmallVector<Value, 2>{notLhs, rhs});
235 case BinaryAssertionOperator::OverlappedImplication:
236 return ltl::ImplicationOp::create(builder, loc, operands);
237 case BinaryAssertionOperator::NonOverlappedImplication: {
241 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
242 builder.getI64IntegerAttr(0));
243 auto antecedent = ltl::ConcatOp::create(
244 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
245 return ltl::ImplicationOp::create(builder, loc,
246 SmallVector<Value, 2>{antecedent, rhs});
248 case BinaryAssertionOperator::OverlappedFollowedBy: {
249 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
250 auto implication = ltl::ImplicationOp::create(
251 builder, loc, SmallVector<Value, 2>{lhs, notRhs});
252 return ltl::NotOp::create(builder, loc, implication);
254 case BinaryAssertionOperator::NonOverlappedFollowedBy: {
257 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
259 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
260 builder.getI64IntegerAttr(0));
261 auto antecedent = ltl::ConcatOp::create(
262 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
263 auto implication = ltl::ImplicationOp::create(
264 builder, loc, SmallVector<Value, 2>{antecedent, notRhs});
265 return ltl::NotOp::create(builder, loc, implication);
267 case BinaryAssertionOperator::SUntil:
268 case BinaryAssertionOperator::SUntilWith:
269 mlir::emitError(loc,
"unsupported binary operator: ")
270 << slang::ast::toString(expr.op);
273 llvm_unreachable(
"All enum values handled in switch");
276 Value visit(
const slang::ast::ClockingAssertionExpr &expr) {
284 template <
typename T>
285 Value visit(T &&node) {
286 mlir::emitError(loc,
"unsupported expression: ")
287 << slang::ast::toString(node.kind);
291 Value visitInvalid(
const slang::ast::AssertionExpr &expr) {
292 mlir::emitError(loc,
"invalid expression");
298FailureOr<Value> Context::convertAssertionSystemCallArity1(
299 const slang::ast::SystemSubroutine &subroutine, Location loc, Value value) {
302 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
306 auto current = value;
308 ltl::PastOp::create(
builder, loc, value, 1).getResult();
310 ltl::NotOp::create(
builder, loc, current).getResult();
311 auto pastAndNotCurrent =
312 ltl::AndOp::create(
builder, loc, {notCurrent, past})
314 return pastAndNotCurrent;
320 ltl::PastOp::create(
builder, loc, value, 1).getResult();
322 auto current = value;
323 auto notPastAndCurrent =
324 ltl::AndOp::create(
builder, loc, {current, notPast})
326 return notPastAndCurrent;
332 ltl::PastOp::create(
builder, loc, value, 1).getResult();
334 ltl::NotOp::create(
builder, loc, past).getResult();
335 auto current = value;
337 ltl::NotOp::create(
builder, loc, value).getResult();
338 auto pastAndCurrent =
339 ltl::AndOp::create(
builder, loc, {current, past})
341 auto notPastAndNotCurrent =
342 ltl::AndOp::create(
builder, loc, {notCurrent, notPast})
345 ltl::OrOp::create(
builder, loc,
346 {pastAndCurrent, notPastAndNotCurrent})
353 ltl::PastOp::create(
builder, loc, value, 1).getResult();
356 .Default([&]() -> Value {
return {}; });
357 return systemCallRes();
361 const slang::ast::CallExpression &expr,
362 const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
364 const auto &subroutine = *info.subroutine;
365 auto args = expr.arguments();
367 FailureOr<Value> result;
370 moore::IntType valTy;
372 switch (args.size()) {
375 valTy = dyn_cast<moore::IntType>(value.getType());
377 mlir::emitError(loc) <<
"expected integer argument for system call `"
378 << subroutine.name <<
"`";
381 if (subroutine.name ==
"$past") {
384 if (
auto ty = dyn_cast<moore::IntType>(value.getType());
385 !ty || ty.getBitSize() != 1) {
386 mlir::emitError(loc,
"$past is currently only supported for 1-wide "
393 if (valTy.getDomain() == Domain::FourValued) {
394 value =
builder.createOrFold<moore::LogicToIntOp>(loc, value);
396 intVal =
builder.createOrFold<moore::ToBuiltinIntOp>(loc, value);
411 mlir::emitError(loc) <<
"unsupported system call `" << subroutine.name <<
"`";
417 AssertionExprVisitor visitor{*
this, loc};
418 return expr.visit(visitor);
426 auto type = dyn_cast<moore::IntType>(value.getType());
427 if (!type || type.getBitSize() != 1) {
428 mlir::emitError(value.getLoc(),
"expected a 1-bit integer");
432 return moore::ToBuiltinBoolOp::create(
builder, value.getLoc(), value);
assert(baseType &&"element must be base type")
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
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={})
FailureOr< Value > convertAssertionSystemCallArity1(const slang::ast::SystemSubroutine &subroutine, Location loc, Value value)
Convert system function calls within properties and assertion with a single argument.
Value convertAssertionExpression(const slang::ast::AssertionExpr &expr, Location loc)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.