9#include "slang/ast/expressions/AssertionExpr.h"
16#include "mlir/IR/BuiltinAttributes.h"
17#include "mlir/Support/LLVM.h"
18#include "slang/analysis/AnalysisManager.h"
19#include "slang/analysis/AnalyzedAssertion.h"
20#include "slang/ast/ASTVisitor.h"
21#include "slang/ast/SystemSubroutine.h"
22#include "slang/parsing/KnownSystemName.h"
28using namespace ImportVerilog;
32struct AssertionExprVisitor {
37 AssertionExprVisitor(
Context &context, Location loc)
38 : context(context), loc(loc), builder(context.builder) {}
41 std::pair<mlir::IntegerAttr, mlir::IntegerAttr>
42 convertRangeToAttrs(uint32_t min,
43 std::optional<uint32_t> max = std::nullopt) {
44 auto minAttr = builder.getI64IntegerAttr(min);
45 mlir::IntegerAttr rangeAttr;
46 if (max.has_value()) {
47 rangeAttr = builder.getI64IntegerAttr(max.value() - min);
49 return {minAttr, rangeAttr};
53 Value createRepetition(Location loc,
54 const slang::ast::SequenceRepetition &repetition,
55 Value &inputSequence) {
57 auto [minRepetitions, repetitionRange] =
58 convertRangeToAttrs(repetition.range.min, repetition.range.max);
60 using slang::ast::SequenceRepetition;
63 if ((repetition.kind == SequenceRepetition::Nonconsecutive ||
64 repetition.kind == SequenceRepetition::GoTo) &&
67 repetition.kind == SequenceRepetition::Nonconsecutive
68 ?
"Nonconsecutive repetition requires a maximum value"
69 :
"GoTo repetition requires a maximum value");
73 switch (repetition.kind) {
74 case SequenceRepetition::Consecutive:
75 return ltl::RepeatOp::create(builder, loc, inputSequence, minRepetitions,
77 case SequenceRepetition::Nonconsecutive:
78 return ltl::NonConsecutiveRepeatOp::create(
79 builder, loc, inputSequence, minRepetitions, repetitionRange);
80 case SequenceRepetition::GoTo:
81 return ltl::GoToRepeatOp::create(builder, loc, inputSequence,
82 minRepetitions, repetitionRange);
84 llvm_unreachable(
"All enum values handled in switch");
87 Value visit(
const slang::ast::SimpleAssertionExpr &expr) {
93 auto valueType = value.getType();
96 if (!mlir::isa<ltl::SequenceType, ltl::PropertyType, mlir::IntegerType>(
106 if (!expr.repetition.has_value()) {
112 return createRepetition(loc, expr.repetition.value(), value);
115 Value visit(
const slang::ast::SequenceConcatExpr &expr) {
117 assert(!expr.elements.empty());
119 SmallVector<Value> sequenceElements;
121 for (
const auto &concatElement : expr.elements) {
122 Value sequenceValue =
127 [[maybe_unused]] Type valueType = sequenceValue.getType();
128 assert(valueType.isInteger(1) || mlir::isa<ltl::SequenceType>(valueType));
130 auto [delayMin, delayRange] =
131 convertRangeToAttrs(concatElement.delay.min, concatElement.delay.max);
132 auto delayedSequence = ltl::DelayOp::create(builder, loc, sequenceValue,
133 delayMin, delayRange);
134 sequenceElements.push_back(delayedSequence);
137 return builder.createOrFold<ltl::ConcatOp>(loc, sequenceElements);
140 Value visit(
const slang::ast::UnaryAssertionExpr &expr) {
144 using slang::ast::UnaryAssertionOperator;
146 case UnaryAssertionOperator::Not:
147 return ltl::NotOp::create(builder, loc, value);
148 case UnaryAssertionOperator::SEventually:
149 if (expr.range.has_value()) {
150 mlir::emitError(loc,
"Strong eventually with range not supported");
153 return ltl::EventuallyOp::create(builder, loc, value);
155 case UnaryAssertionOperator::Always: {
156 std::pair<mlir::IntegerAttr, mlir::IntegerAttr> attr = {
157 builder.getI64IntegerAttr(0), mlir::IntegerAttr{}};
158 if (expr.range.has_value()) {
160 convertRangeToAttrs(expr.range.value().min, expr.range.value().max);
162 return ltl::RepeatOp::create(builder, loc, value, attr.first,
165 case UnaryAssertionOperator::NextTime: {
166 auto minRepetitions = builder.getI64IntegerAttr(1);
167 if (expr.range.has_value()) {
168 minRepetitions = builder.getI64IntegerAttr(expr.range.value().min);
170 return ltl::DelayOp::create(builder, loc, value, minRepetitions,
171 builder.getI64IntegerAttr(0));
173 case UnaryAssertionOperator::Eventually:
174 case UnaryAssertionOperator::SNextTime:
175 case UnaryAssertionOperator::SAlways:
176 mlir::emitError(loc,
"unsupported unary operator: ")
177 << slang::ast::toString(expr.op);
180 llvm_unreachable(
"All enum values handled in switch");
183 Value visit(
const slang::ast::BinaryAssertionExpr &expr) {
188 SmallVector<Value, 2> operands = {lhs, rhs};
189 using slang::ast::BinaryAssertionOperator;
191 case BinaryAssertionOperator::And:
192 return ltl::AndOp::create(builder, loc, operands);
193 case BinaryAssertionOperator::Or:
194 return ltl::OrOp::create(builder, loc, operands);
195 case BinaryAssertionOperator::Intersect:
196 return ltl::IntersectOp::create(builder, loc, operands);
197 case BinaryAssertionOperator::Throughout: {
198 auto lhsRepeat = ltl::RepeatOp::create(
199 builder, loc, lhs, builder.getI64IntegerAttr(0), mlir::IntegerAttr{});
200 return ltl::IntersectOp::create(builder, loc,
201 SmallVector<Value, 2>{lhsRepeat, rhs});
203 case BinaryAssertionOperator::Within: {
206 auto oneRepeat = ltl::RepeatOp::create(builder, loc, constOne,
207 builder.getI64IntegerAttr(0),
208 mlir::IntegerAttr{});
209 auto repeatDelay = ltl::DelayOp::create(builder, loc, oneRepeat,
210 builder.getI64IntegerAttr(1),
211 builder.getI64IntegerAttr(0));
213 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
214 builder.getI64IntegerAttr(0));
215 auto combined = ltl::ConcatOp::create(
216 builder, loc, SmallVector<Value, 3>{repeatDelay, lhsDelay, constOne});
217 return ltl::IntersectOp::create(builder, loc,
218 SmallVector<Value, 2>{combined, rhs});
220 case BinaryAssertionOperator::Iff: {
221 auto ored = ltl::OrOp::create(builder, loc, operands);
222 auto notOred = ltl::NotOp::create(builder, loc, ored);
223 auto anded = ltl::AndOp::create(builder, loc, operands);
224 return ltl::OrOp::create(builder, loc,
225 SmallVector<Value, 2>{notOred, anded});
227 case BinaryAssertionOperator::Until:
228 return ltl::UntilOp::create(builder, loc, operands);
229 case BinaryAssertionOperator::UntilWith: {
230 auto untilOp = ltl::UntilOp::create(builder, loc, operands);
231 auto andOp = ltl::AndOp::create(builder, loc, operands);
232 auto notUntil = ltl::NotOp::create(builder, loc, untilOp);
233 return ltl::OrOp::create(builder, loc,
234 SmallVector<Value, 2>{notUntil, andOp});
236 case BinaryAssertionOperator::Implies: {
237 auto notLhs = ltl::NotOp::create(builder, loc, lhs);
238 return ltl::OrOp::create(builder, loc,
239 SmallVector<Value, 2>{notLhs, rhs});
241 case BinaryAssertionOperator::OverlappedImplication:
242 return ltl::ImplicationOp::create(builder, loc, operands);
243 case BinaryAssertionOperator::NonOverlappedImplication: {
247 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
248 builder.getI64IntegerAttr(0));
249 auto antecedent = ltl::ConcatOp::create(
250 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
251 return ltl::ImplicationOp::create(builder, loc,
252 SmallVector<Value, 2>{antecedent, rhs});
254 case BinaryAssertionOperator::OverlappedFollowedBy: {
255 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
256 auto implication = ltl::ImplicationOp::create(
257 builder, loc, SmallVector<Value, 2>{lhs, notRhs});
258 return ltl::NotOp::create(builder, loc, implication);
260 case BinaryAssertionOperator::NonOverlappedFollowedBy: {
263 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
265 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
266 builder.getI64IntegerAttr(0));
267 auto antecedent = ltl::ConcatOp::create(
268 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
269 auto implication = ltl::ImplicationOp::create(
270 builder, loc, SmallVector<Value, 2>{antecedent, notRhs});
271 return ltl::NotOp::create(builder, loc, implication);
273 case BinaryAssertionOperator::SUntil:
274 case BinaryAssertionOperator::SUntilWith:
275 mlir::emitError(loc,
"unsupported binary operator: ")
276 << slang::ast::toString(expr.op);
279 llvm_unreachable(
"All enum values handled in switch");
282 Value visit(
const slang::ast::ClockingAssertionExpr &expr) {
290 template <
typename T>
291 Value visit(T &&node) {
292 mlir::emitError(loc,
"unsupported expression: ")
293 << slang::ast::toString(node.kind);
297 Value visitInvalid(
const slang::ast::AssertionExpr &expr) {
298 mlir::emitError(loc,
"invalid expression");
304FailureOr<Value> Context::convertAssertionSystemCallArity1(
305 const slang::ast::SystemSubroutine &subroutine, Location loc, Value value,
306 Type originalType, Value clockVal) {
307 using ksn = slang::parsing::KnownSystemName;
308 auto nameId = subroutine.knownNameId;
311 auto castToMoore = [&](Value v) -> Value {
312 if (
auto ty = dyn_cast<moore::IntType>(originalType)) {
313 v = moore::FromBuiltinIntOp::create(
builder, loc, v);
314 if (ty.getDomain() == Domain::FourValued)
315 v = moore::IntToLogicOp::create(
builder, loc, v);
321 auto castToTwoValued = [&](Value v) -> Value {
322 return moore::FromBuiltinIntOp::create(
builder, loc, v);
327 return castToMoore(ltl::SampledOp::create(
builder, loc, value));
332 ltl::PastOp::create(
builder, loc, value, 1, clockVal).getResult();
333 return castToTwoValued(comb::ICmpOp::create(
334 builder, loc, comb::ICmpPredicate::ugt, past, value,
false));
340 ltl::PastOp::create(
builder, loc, value, 1, clockVal).getResult();
341 return castToTwoValued(comb::ICmpOp::create(
342 builder, loc, comb::ICmpPredicate::ult, past, value,
false));
348 ltl::PastOp::create(
builder, loc, value, 1, clockVal).getResult();
349 return castToTwoValued(comb::ICmpOp::create(
350 builder, loc, comb::ICmpPredicate::ne, past, value,
false));
356 ltl::PastOp::create(
builder, loc, value, 1, clockVal).getResult();
357 return castToTwoValued(comb::ICmpOp::create(
358 builder, loc, comb::ICmpPredicate::eq, past, value,
false));
362 return castToMoore(ltl::PastOp::create(
builder, loc, value, 1, clockVal));
370 const slang::ast::CallExpression &expr,
371 const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
373 const slang::ast::TimingControl *clock =
nullptr;
376 clock = clockIt->second;
380 const slang::ast::SignalEventControl *signal =
nullptr;
381 if (clock->kind == slang::ast::TimingControlKind::SignalEvent) {
382 signal = &clock->as<slang::ast::SignalEventControl>();
383 }
else if (clock->kind == slang::ast::TimingControlKind::EventList) {
384 mlir::emitError(loc,
"sampled value functions with multiple event "
385 "triggers are not supported");
388 llvm_unreachable(
"unexpected clock kind for assertion");
391 if (signal->edge != slang::ast::EdgeKind::PosEdge) {
394 "sampled value functions are only supported with posedge clocks");
404 const auto &subroutine = *info.subroutine;
405 auto args = expr.arguments();
407 FailureOr<Value> result;
411 moore::IntType valTy;
413 switch (args.size()) {
416 originalType = value.getType();
417 valTy = dyn_cast<moore::IntType>(value.getType());
419 mlir::emitError(loc) <<
"expected integer argument for `"
420 << subroutine.name <<
"`";
426 if (valTy.getDomain() == Domain::FourValued) {
427 value =
builder.createOrFold<moore::LogicToIntOp>(loc, value);
429 intVal =
builder.createOrFold<moore::ToBuiltinIntOp>(loc, value);
433 originalType, clockVal);
448 mlir::emitError(loc) <<
"unsupported system call `" << subroutine.name <<
"`";
454 AssertionExprVisitor visitor{*
this, loc};
455 return expr.visit(visitor);
463 auto loc = value.getLoc();
464 auto type = dyn_cast<moore::IntType>(value.getType());
465 if (!type || type.getBitSize() != 1) {
466 mlir::emitError(loc,
"expected a 1-bit integer");
469 if (type.getDomain() == Domain::FourValued) {
470 value = moore::LogicToIntOp::create(
builder, loc, value);
472 return moore::ToBuiltinIntOp::create(
builder, loc, value);
476struct AssertionClockVisitor
477 : slang::ast::ASTVisitor<AssertionClockVisitor,
478 slang::ast::VisitFlags::AllGood> {
480 const slang::analysis::AnalyzedAssertion &assertion;
481 const slang::ast::TimingControl *currentClock =
nullptr;
484 const slang::analysis::AnalyzedAssertion &assertion)
487 void handle(
const slang::ast::CallExpression &node) {
489 context.assertionCallClocks[&node] = currentClock;
493 template <
typename T>
494 std::enable_if_t<std::is_base_of_v<slang::ast::AssertionExpr, T>>
495 handle(
const T &node) {
496 auto *prevClock = currentClock;
497 if (
auto *clk = assertion.getClock(node))
500 currentClock = prevClock;
507 slang::analysis::AnalysisManager am;
508 am.addListener([
this](
const slang::analysis::AnalyzedAssertion &assertion) {
509 AssertionClockVisitor visitor{*
this, assertion};
510 assertion.getRoot().visit(visitor);
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
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)
slang::ast::Compilation & compilation
OpBuilder builder
The builder used to create IR operations.
Value convertAssertionCallExpression(const slang::ast::CallExpression &expr, const slang::ast::CallExpression::SystemCallInfo &info, Location loc)
FailureOr< Value > convertAssertionSystemCallArity1(const slang::ast::SystemSubroutine &subroutine, Location loc, Value value, Type originalType, Value clockVal)
Convert system function calls within properties and assertion with a single argument.
Type convertType(const slang::ast::Type &type, LocationAttr loc={})
Convert a slang type into an MLIR type.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
Value materializeConversion(Type type, Value value, bool isSigned, Location loc, bool fallible=false)
Helper function to insert the necessary operations to cast a value from one type to another.
void populateAssertionClocks()
Generates a map from assertions to clocks using Slang's analysis.
DenseMap< const slang::ast::CallExpression *, const slang::ast::TimingControl * > assertionCallClocks
Maps assertion system calls to their corresponding clocks.
Value convertAssertionExpression(const slang::ast::AssertionExpr &expr, Location loc)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.