9#include "slang/ast/expressions/AssertionExpr.h"
14#include "mlir/IR/BuiltinAttributes.h"
15#include "mlir/Support/LLVM.h"
16#include "slang/ast/SystemSubroutine.h"
22using namespace ImportVerilog;
26struct AssertionExprVisitor {
31 AssertionExprVisitor(
Context &context, Location loc)
32 : context(context), loc(loc), builder(context.builder) {}
35 std::pair<mlir::IntegerAttr, mlir::IntegerAttr>
36 convertRangeToAttrs(uint32_t min,
37 std::optional<uint32_t> max = std::nullopt) {
38 auto minAttr = builder.getI64IntegerAttr(min);
39 mlir::IntegerAttr rangeAttr;
40 if (max.has_value()) {
41 rangeAttr = builder.getI64IntegerAttr(max.value() - min);
43 return {minAttr, rangeAttr};
47 Value createRepetition(Location loc,
48 const slang::ast::SequenceRepetition &repetition,
49 Value &inputSequence) {
51 auto [minRepetitions, repetitionRange] =
52 convertRangeToAttrs(repetition.range.min, repetition.range.max);
54 using slang::ast::SequenceRepetition;
57 if ((repetition.kind == SequenceRepetition::Nonconsecutive ||
58 repetition.kind == SequenceRepetition::GoTo) &&
61 repetition.kind == SequenceRepetition::Nonconsecutive
62 ?
"Nonconsecutive repetition requires a maximum value"
63 :
"GoTo repetition requires a maximum value");
67 switch (repetition.kind) {
68 case SequenceRepetition::Consecutive:
69 return ltl::RepeatOp::create(builder, loc, inputSequence, minRepetitions,
71 case SequenceRepetition::Nonconsecutive:
72 return ltl::NonConsecutiveRepeatOp::create(
73 builder, loc, inputSequence, minRepetitions, repetitionRange);
74 case SequenceRepetition::GoTo:
75 return ltl::GoToRepeatOp::create(builder, loc, inputSequence,
76 minRepetitions, repetitionRange);
78 llvm_unreachable(
"All enum values handled in switch");
81 Value visit(
const slang::ast::SimpleAssertionExpr &expr) {
87 auto valueType = value.getType();
90 if (!mlir::isa<ltl::SequenceType, ltl::PropertyType>(valueType)) {
98 if (!expr.repetition.has_value()) {
104 return createRepetition(loc, expr.repetition.value(), value);
107 Value visit(
const slang::ast::SequenceConcatExpr &expr) {
109 assert(!expr.elements.empty());
111 SmallVector<Value> sequenceElements;
113 for (
const auto &concatElement : expr.elements) {
114 Value sequenceValue =
119 [[maybe_unused]] Type valueType = sequenceValue.getType();
120 assert(valueType.isInteger(1) || mlir::isa<ltl::SequenceType>(valueType));
122 auto [delayMin, delayRange] =
123 convertRangeToAttrs(concatElement.delay.min, concatElement.delay.max);
124 auto delayedSequence = ltl::DelayOp::create(builder, loc, sequenceValue,
125 delayMin, delayRange);
126 sequenceElements.push_back(delayedSequence);
129 return builder.createOrFold<ltl::ConcatOp>(loc, sequenceElements);
132 Value visit(
const slang::ast::UnaryAssertionExpr &expr) {
136 using slang::ast::UnaryAssertionOperator;
138 case UnaryAssertionOperator::Not:
139 return ltl::NotOp::create(builder, loc, value);
140 case UnaryAssertionOperator::SEventually:
141 if (expr.range.has_value()) {
142 mlir::emitError(loc,
"Strong eventually with range not supported");
145 return ltl::EventuallyOp::create(builder, loc, value);
147 case UnaryAssertionOperator::Always: {
148 std::pair<mlir::IntegerAttr, mlir::IntegerAttr> attr = {
149 builder.getI64IntegerAttr(0), mlir::IntegerAttr{}};
150 if (expr.range.has_value()) {
152 convertRangeToAttrs(expr.range.value().min, expr.range.value().max);
154 return ltl::RepeatOp::create(builder, loc, value, attr.first,
157 case UnaryAssertionOperator::NextTime: {
158 auto minRepetitions = builder.getI64IntegerAttr(1);
159 if (expr.range.has_value()) {
160 minRepetitions = builder.getI64IntegerAttr(expr.range.value().min);
162 return ltl::DelayOp::create(builder, loc, value, minRepetitions,
163 builder.getI64IntegerAttr(0));
165 case UnaryAssertionOperator::Eventually:
166 case UnaryAssertionOperator::SNextTime:
167 case UnaryAssertionOperator::SAlways:
168 mlir::emitError(loc,
"unsupported unary operator: ")
169 << slang::ast::toString(expr.op);
172 llvm_unreachable(
"All enum values handled in switch");
175 Value visit(
const slang::ast::BinaryAssertionExpr &expr) {
180 SmallVector<Value, 2> operands = {lhs, rhs};
181 using slang::ast::BinaryAssertionOperator;
183 case BinaryAssertionOperator::And:
184 return ltl::AndOp::create(builder, loc, operands);
185 case BinaryAssertionOperator::Or:
186 return ltl::OrOp::create(builder, loc, operands);
187 case BinaryAssertionOperator::Intersect:
188 return ltl::IntersectOp::create(builder, loc, operands);
189 case BinaryAssertionOperator::Throughout: {
190 auto lhsRepeat = ltl::RepeatOp::create(
191 builder, loc, lhs, builder.getI64IntegerAttr(0), mlir::IntegerAttr{});
192 return ltl::IntersectOp::create(builder, loc,
193 SmallVector<Value, 2>{lhsRepeat, rhs});
195 case BinaryAssertionOperator::Within: {
198 auto oneRepeat = ltl::RepeatOp::create(builder, loc, constOne,
199 builder.getI64IntegerAttr(0),
200 mlir::IntegerAttr{});
201 auto repeatDelay = ltl::DelayOp::create(builder, loc, oneRepeat,
202 builder.getI64IntegerAttr(1),
203 builder.getI64IntegerAttr(0));
205 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
206 builder.getI64IntegerAttr(0));
207 auto combined = ltl::ConcatOp::create(
208 builder, loc, SmallVector<Value, 3>{repeatDelay, lhsDelay, constOne});
209 return ltl::IntersectOp::create(builder, loc,
210 SmallVector<Value, 2>{combined, rhs});
212 case BinaryAssertionOperator::Iff: {
213 auto ored = ltl::OrOp::create(builder, loc, operands);
214 auto notOred = ltl::NotOp::create(builder, loc, ored);
215 auto anded = ltl::AndOp::create(builder, loc, operands);
216 return ltl::OrOp::create(builder, loc,
217 SmallVector<Value, 2>{notOred, anded});
219 case BinaryAssertionOperator::Until:
220 return ltl::UntilOp::create(builder, loc, operands);
221 case BinaryAssertionOperator::UntilWith: {
222 auto untilOp = ltl::UntilOp::create(builder, loc, operands);
223 auto andOp = ltl::AndOp::create(builder, loc, operands);
224 auto notUntil = ltl::NotOp::create(builder, loc, untilOp);
225 return ltl::OrOp::create(builder, loc,
226 SmallVector<Value, 2>{notUntil, andOp});
228 case BinaryAssertionOperator::Implies: {
229 auto notLhs = ltl::NotOp::create(builder, loc, lhs);
230 return ltl::OrOp::create(builder, loc,
231 SmallVector<Value, 2>{notLhs, rhs});
233 case BinaryAssertionOperator::OverlappedImplication:
234 return ltl::ImplicationOp::create(builder, loc, operands);
235 case BinaryAssertionOperator::NonOverlappedImplication: {
239 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
240 builder.getI64IntegerAttr(0));
241 auto antecedent = ltl::ConcatOp::create(
242 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
243 return ltl::ImplicationOp::create(builder, loc,
244 SmallVector<Value, 2>{antecedent, rhs});
246 case BinaryAssertionOperator::OverlappedFollowedBy: {
247 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
248 auto implication = ltl::ImplicationOp::create(
249 builder, loc, SmallVector<Value, 2>{lhs, notRhs});
250 return ltl::NotOp::create(builder, loc, implication);
252 case BinaryAssertionOperator::NonOverlappedFollowedBy: {
255 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
257 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
258 builder.getI64IntegerAttr(0));
259 auto antecedent = ltl::ConcatOp::create(
260 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
261 auto implication = ltl::ImplicationOp::create(
262 builder, loc, SmallVector<Value, 2>{antecedent, notRhs});
263 return ltl::NotOp::create(builder, loc, implication);
265 case BinaryAssertionOperator::SUntil:
266 case BinaryAssertionOperator::SUntilWith:
267 mlir::emitError(loc,
"unsupported binary operator: ")
268 << slang::ast::toString(expr.op);
271 llvm_unreachable(
"All enum values handled in switch");
274 Value visit(
const slang::ast::ClockingAssertionExpr &expr) {
282 template <
typename T>
283 Value visit(T &&node) {
284 mlir::emitError(loc,
"unsupported expression: ")
285 << slang::ast::toString(node.kind);
289 Value visitInvalid(
const slang::ast::AssertionExpr &expr) {
290 mlir::emitError(loc,
"invalid expression");
296FailureOr<Value> Context::convertAssertionSystemCallArity1(
297 const slang::ast::SystemSubroutine &subroutine, Location loc, Value value) {
300 llvm::StringSwitch<std::function<FailureOr<Value>()>>(subroutine.name)
304 auto current = value;
306 ltl::PastOp::create(
builder, loc, value, 1).getResult();
308 ltl::NotOp::create(
builder, loc, current).getResult();
309 auto pastAndNotCurrent =
310 ltl::AndOp::create(
builder, loc, {notCurrent, past})
312 return pastAndNotCurrent;
318 ltl::PastOp::create(
builder, loc, value, 1).getResult();
320 ltl::NotOp::create(
builder, loc, past).getResult();
321 auto current = value;
322 auto notPastAndCurrent =
323 ltl::AndOp::create(
builder, loc, {current, notPast})
325 return notPastAndCurrent;
331 ltl::PastOp::create(
builder, loc, value, 1).getResult();
333 ltl::NotOp::create(
builder, loc, past).getResult();
334 auto current = value;
336 ltl::NotOp::create(
builder, loc, value).getResult();
337 auto pastAndCurrent =
338 ltl::AndOp::create(
builder, loc, {current, past})
340 auto notPastAndNotCurrent =
341 ltl::AndOp::create(
builder, loc, {notCurrent, notPast})
344 ltl::OrOp::create(
builder, loc,
345 {pastAndCurrent, notPastAndNotCurrent})
349 .Default([&]() -> Value {
return {}; });
350 return systemCallRes();
354 const slang::ast::CallExpression &expr,
355 const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
357 const auto &subroutine = *info.subroutine;
358 auto args = expr.arguments();
360 FailureOr<Value> result;
364 switch (args.size()) {
367 boolVal =
builder.createOrFold<moore::ToBuiltinBoolOp>(loc, value);
382 mlir::emitError(loc) <<
"unsupported system call `" << subroutine.name <<
"`";
388 AssertionExprVisitor visitor{*
this, loc};
389 return expr.visit(visitor);
397 auto type = dyn_cast<moore::IntType>(value.getType());
398 if (!type || type.getBitSize() != 1) {
399 mlir::emitError(value.getLoc(),
"expected a 1-bit integer");
403 return moore::ToBuiltinBoolOp::create(
builder, value.getLoc(), 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={})
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.