CIRCT 23.0.0git
Loading...
Searching...
No Matches
AssertionExpr.cpp
Go to the documentation of this file.
1//===- AssertionExpr.cpp - Slang assertion expression conversion ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "slang/ast/expressions/AssertionExpr.h"
10
16#include "circt/Support/FVInt.h"
17#include "circt/Support/LLVM.h"
18#include "mlir/IR/BuiltinAttributes.h"
19#include "mlir/Support/LLVM.h"
20#include "slang/analysis/AnalysisManager.h"
21#include "slang/analysis/AnalyzedAssertion.h"
22#include "slang/ast/ASTVisitor.h"
23#include "slang/ast/SystemSubroutine.h"
24#include "slang/parsing/KnownSystemName.h"
25
26#include <optional>
27#include <utility>
28
29using namespace circt;
30using namespace ImportVerilog;
31
32// NOLINTBEGIN(misc-no-recursion)
33namespace {
34struct AssertionExprVisitor {
35 Context &context;
36 Location loc;
37 OpBuilder &builder;
38
39 AssertionExprVisitor(Context &context, Location loc)
40 : context(context), loc(loc), builder(context.builder) {}
41
42 /// Helper to convert a range (min, optional max) to MLIR integer attributes
43 std::pair<mlir::IntegerAttr, mlir::IntegerAttr>
44 convertRangeToAttrs(uint32_t min,
45 std::optional<uint32_t> max = std::nullopt) {
46 auto minAttr = builder.getI64IntegerAttr(min);
47 mlir::IntegerAttr rangeAttr;
48 if (max.has_value()) {
49 rangeAttr = builder.getI64IntegerAttr(max.value() - min);
50 }
51 return {minAttr, rangeAttr};
52 }
53
54 /// Add repetition operation to a sequence
55 Value createRepetition(Location loc,
56 const slang::ast::SequenceRepetition &repetition,
57 Value &inputSequence) {
58 // Extract cycle range
59 auto [minRepetitions, repetitionRange] =
60 convertRangeToAttrs(repetition.range.min, repetition.range.max);
61
62 using slang::ast::SequenceRepetition;
63
64 // Check if repetition range is required
65 if ((repetition.kind == SequenceRepetition::Nonconsecutive ||
66 repetition.kind == SequenceRepetition::GoTo) &&
67 !repetitionRange) {
68 mlir::emitError(loc,
69 repetition.kind == SequenceRepetition::Nonconsecutive
70 ? "Nonconsecutive repetition requires a maximum value"
71 : "GoTo repetition requires a maximum value");
72 return {};
73 }
74
75 switch (repetition.kind) {
76 case SequenceRepetition::Consecutive:
77 return ltl::RepeatOp::create(builder, loc, inputSequence, minRepetitions,
78 repetitionRange);
79 case SequenceRepetition::Nonconsecutive:
80 return ltl::NonConsecutiveRepeatOp::create(
81 builder, loc, inputSequence, minRepetitions, repetitionRange);
82 case SequenceRepetition::GoTo:
83 return ltl::GoToRepeatOp::create(builder, loc, inputSequence,
84 minRepetitions, repetitionRange);
85 }
86 llvm_unreachable("All enum values handled in switch");
87 }
88
89 Value visit(const slang::ast::SimpleAssertionExpr &expr) {
90 // Handle expression
91 auto value = context.convertRvalueExpression(expr.expr);
92 if (!value)
93 return {};
94 auto loc = context.convertLocation(expr.expr.sourceRange);
95 auto valueType = value.getType();
96 // For assertion instances the value is already the expected type, convert
97 // boolean value
98 if (!mlir::isa<ltl::SequenceType, ltl::PropertyType, mlir::IntegerType>(
99 valueType)) {
100 value = context.convertToI1(value);
101 }
102 if (!value)
103 return {};
104
105 // Handle repetition
106 // The optional repetition is empty, return the converted expression
107 if (!expr.repetition.has_value()) {
108 return value;
109 }
110
111 // There is a repetition, embed the expression into the kind of given
112 // repetition
113 return createRepetition(loc, expr.repetition.value(), value);
114 }
115
116 Value visit(const slang::ast::SequenceConcatExpr &expr) {
117 // Create a sequence of delayed operations, combined with a concat operation
118 assert(!expr.elements.empty());
119
120 SmallVector<Value> sequenceElements;
121
122 for (const auto &concatElement : expr.elements) {
123 Value sequenceValue =
124 context.convertAssertionExpression(*concatElement.sequence, loc);
125 if (!sequenceValue)
126 return {};
127
128 [[maybe_unused]] Type valueType = sequenceValue.getType();
129 assert(valueType.isInteger(1) || mlir::isa<ltl::SequenceType>(valueType));
130
131 auto [delayMin, delayRange] =
132 convertRangeToAttrs(concatElement.delay.min, concatElement.delay.max);
133 auto delayedSequence = ltl::DelayOp::create(builder, loc, sequenceValue,
134 delayMin, delayRange);
135 sequenceElements.push_back(delayedSequence);
136 }
137
138 return builder.createOrFold<ltl::ConcatOp>(loc, sequenceElements);
139 }
140
141 Value visit(const slang::ast::UnaryAssertionExpr &expr) {
142 auto value = context.convertAssertionExpression(expr.expr, loc);
143 if (!value)
144 return {};
145 using slang::ast::UnaryAssertionOperator;
146 switch (expr.op) {
147 case UnaryAssertionOperator::Not:
148 return ltl::NotOp::create(builder, loc, value);
149 case UnaryAssertionOperator::SEventually:
150 if (expr.range.has_value()) {
151 mlir::emitError(loc, "Strong eventually with range not supported");
152 return {};
153 } else {
154 return ltl::EventuallyOp::create(builder, loc, value);
155 }
156 case UnaryAssertionOperator::Always: {
157 std::pair<mlir::IntegerAttr, mlir::IntegerAttr> attr = {
158 builder.getI64IntegerAttr(0), mlir::IntegerAttr{}};
159 if (expr.range.has_value()) {
160 attr =
161 convertRangeToAttrs(expr.range.value().min, expr.range.value().max);
162 }
163 return ltl::RepeatOp::create(builder, loc, value, attr.first,
164 attr.second);
165 }
166 case UnaryAssertionOperator::NextTime: {
167 auto minRepetitions = builder.getI64IntegerAttr(1);
168 if (expr.range.has_value()) {
169 minRepetitions = builder.getI64IntegerAttr(expr.range.value().min);
170 }
171 return ltl::DelayOp::create(builder, loc, value, minRepetitions,
172 builder.getI64IntegerAttr(0));
173 }
174 case UnaryAssertionOperator::Eventually:
175 case UnaryAssertionOperator::SNextTime:
176 case UnaryAssertionOperator::SAlways:
177 mlir::emitError(loc, "unsupported unary operator: ")
178 << slang::ast::toString(expr.op);
179 return {};
180 }
181 llvm_unreachable("All enum values handled in switch");
182 }
183
184 Value visit(const slang::ast::BinaryAssertionExpr &expr) {
185 auto lhs = context.convertAssertionExpression(expr.left, loc);
186 auto rhs = context.convertAssertionExpression(expr.right, loc);
187 if (!lhs || !rhs)
188 return {};
189 SmallVector<Value, 2> operands = {lhs, rhs};
190 using slang::ast::BinaryAssertionOperator;
191 switch (expr.op) {
192 case BinaryAssertionOperator::And:
193 return ltl::AndOp::create(builder, loc, operands);
194 case BinaryAssertionOperator::Or:
195 return ltl::OrOp::create(builder, loc, operands);
196 case BinaryAssertionOperator::Intersect:
197 return ltl::IntersectOp::create(builder, loc, operands);
198 case BinaryAssertionOperator::Throughout: {
199 auto lhsRepeat = ltl::RepeatOp::create(
200 builder, loc, lhs, builder.getI64IntegerAttr(0), mlir::IntegerAttr{});
201 return ltl::IntersectOp::create(builder, loc,
202 SmallVector<Value, 2>{lhsRepeat, rhs});
203 }
204 case BinaryAssertionOperator::Within: {
205 auto constOne =
206 hw::ConstantOp::create(builder, loc, builder.getI1Type(), 1);
207 auto oneRepeat = ltl::RepeatOp::create(builder, loc, constOne,
208 builder.getI64IntegerAttr(0),
209 mlir::IntegerAttr{});
210 auto repeatDelay = ltl::DelayOp::create(builder, loc, oneRepeat,
211 builder.getI64IntegerAttr(1),
212 builder.getI64IntegerAttr(0));
213 auto lhsDelay =
214 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
215 builder.getI64IntegerAttr(0));
216 auto combined = ltl::ConcatOp::create(
217 builder, loc, SmallVector<Value, 3>{repeatDelay, lhsDelay, constOne});
218 return ltl::IntersectOp::create(builder, loc,
219 SmallVector<Value, 2>{combined, rhs});
220 }
221 case BinaryAssertionOperator::Iff: {
222 auto ored = ltl::OrOp::create(builder, loc, operands);
223 auto notOred = ltl::NotOp::create(builder, loc, ored);
224 auto anded = ltl::AndOp::create(builder, loc, operands);
225 return ltl::OrOp::create(builder, loc,
226 SmallVector<Value, 2>{notOred, anded});
227 }
228 case BinaryAssertionOperator::Until:
229 return ltl::UntilOp::create(builder, loc, operands);
230 case BinaryAssertionOperator::UntilWith: {
231 auto untilOp = ltl::UntilOp::create(builder, loc, operands);
232 auto andOp = ltl::AndOp::create(builder, loc, operands);
233 auto notUntil = ltl::NotOp::create(builder, loc, untilOp);
234 return ltl::OrOp::create(builder, loc,
235 SmallVector<Value, 2>{notUntil, andOp});
236 }
237 case BinaryAssertionOperator::Implies: {
238 auto notLhs = ltl::NotOp::create(builder, loc, lhs);
239 return ltl::OrOp::create(builder, loc,
240 SmallVector<Value, 2>{notLhs, rhs});
241 }
242 case BinaryAssertionOperator::OverlappedImplication:
243 return ltl::ImplicationOp::create(builder, loc, operands);
244 case BinaryAssertionOperator::NonOverlappedImplication: {
245 auto constOne =
246 hw::ConstantOp::create(builder, loc, builder.getI1Type(), 1);
247 auto lhsDelay =
248 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
249 builder.getI64IntegerAttr(0));
250 auto antecedent = ltl::ConcatOp::create(
251 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
252 return ltl::ImplicationOp::create(builder, loc,
253 SmallVector<Value, 2>{antecedent, rhs});
254 }
255 case BinaryAssertionOperator::OverlappedFollowedBy: {
256 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
257 auto implication = ltl::ImplicationOp::create(
258 builder, loc, SmallVector<Value, 2>{lhs, notRhs});
259 return ltl::NotOp::create(builder, loc, implication);
260 }
261 case BinaryAssertionOperator::NonOverlappedFollowedBy: {
262 auto constOne =
263 hw::ConstantOp::create(builder, loc, builder.getI1Type(), 1);
264 auto notRhs = ltl::NotOp::create(builder, loc, rhs);
265 auto lhsDelay =
266 ltl::DelayOp::create(builder, loc, lhs, builder.getI64IntegerAttr(1),
267 builder.getI64IntegerAttr(0));
268 auto antecedent = ltl::ConcatOp::create(
269 builder, loc, SmallVector<Value, 2>{lhsDelay, constOne});
270 auto implication = ltl::ImplicationOp::create(
271 builder, loc, SmallVector<Value, 2>{antecedent, notRhs});
272 return ltl::NotOp::create(builder, loc, implication);
273 }
274 case BinaryAssertionOperator::SUntil:
275 case BinaryAssertionOperator::SUntilWith:
276 mlir::emitError(loc, "unsupported binary operator: ")
277 << slang::ast::toString(expr.op);
278 return {};
279 }
280 llvm_unreachable("All enum values handled in switch");
281 }
282
283 Value visit(const slang::ast::ClockingAssertionExpr &expr) {
284 auto assertionExpr = context.convertAssertionExpression(expr.expr, loc);
285 if (!assertionExpr)
286 return {};
287 return context.convertLTLTimingControl(expr.clocking, assertionExpr);
288 }
289
290 /// Emit an error for all other expressions.
291 template <typename T>
292 Value visit(T &&node) {
293 mlir::emitError(loc, "unsupported expression: ")
294 << slang::ast::toString(node.kind);
295 return {};
296 }
297
298 Value visitInvalid(const slang::ast::AssertionExpr &expr) {
299 mlir::emitError(loc, "invalid expression");
300 return {};
301 }
302};
303} // namespace
304
305FailureOr<Value> Context::convertAssertionSystemCallArity1(
306 const slang::ast::SystemSubroutine &subroutine, Location loc, Value value,
307 Type originalType, Value clockVal) {
308 using ksn = slang::parsing::KnownSystemName;
309 auto nameId = subroutine.knownNameId;
310
311 // Helper to cast a builtin integer result back to Moore integer types.
312 auto castToMoore = [&](Value v) -> Value {
313 if (auto ty = dyn_cast<moore::IntType>(originalType)) {
314 v = moore::FromBuiltinIntOp::create(builder, loc, v);
315 if (ty.getDomain() == Domain::FourValued)
316 v = moore::IntToLogicOp::create(builder, loc, v);
317 }
318 return v;
319 };
320
321 switch (nameId) {
322 case ksn::Sampled:
323 return castToMoore(ltl::SampledOp::create(builder, loc, value));
324
325 // Translate $fell to ¬x[0] ∧ x[-1]
326 case ksn::Fell: {
327 auto past =
328 ltl::PastOp::create(builder, loc, value, 1, clockVal).getResult();
329 return castToMoore(comb::ICmpOp::create(
330 builder, loc, comb::ICmpPredicate::ugt, past, value, false));
331 }
332
333 // Translate $rose to x[0] ∧ ¬x[-1]
334 case ksn::Rose: {
335 auto past =
336 ltl::PastOp::create(builder, loc, value, 1, clockVal).getResult();
337 return castToMoore(comb::ICmpOp::create(
338 builder, loc, comb::ICmpPredicate::ult, past, value, false));
339 }
340
341 // Translate $changed to x[0] ≠ x[-1]
342 case ksn::Changed: {
343 auto past =
344 ltl::PastOp::create(builder, loc, value, 1, clockVal).getResult();
345 return castToMoore(comb::ICmpOp::create(
346 builder, loc, comb::ICmpPredicate::ne, past, value, false));
347 }
348
349 // Translate $stable to x[0] = x[-1]
350 case ksn::Stable: {
351 auto past =
352 ltl::PastOp::create(builder, loc, value, 1, clockVal).getResult();
353 return castToMoore(comb::ICmpOp::create(
354 builder, loc, comb::ICmpPredicate::eq, past, value, false));
355 }
356
357 case ksn::Past:
358 return castToMoore(ltl::PastOp::create(builder, loc, value, 1, clockVal));
359
360 case ksn::OneHot0: {
361 auto one = hw::ConstantOp::create(builder, loc, value.getType(), 1);
362 auto minusOne = comb::SubOp::create(builder, loc, value, one);
363 auto anded = comb::AndOp::create(builder, loc, value, minusOne);
364 auto zero = hw::ConstantOp::create(builder, loc, value.getType(), 0);
365 return castToMoore(comb::ICmpOp::create(
366 builder, loc, comb::ICmpPredicate::eq, anded, zero, false));
367 }
368
369 case ksn::OneHot: {
370 auto one = hw::ConstantOp::create(builder, loc, value.getType(), 1);
371 auto minusOne = comb::SubOp::create(builder, loc, value, one);
372 auto anded = comb::AndOp::create(builder, loc, value, minusOne);
373 auto zero = hw::ConstantOp::create(builder, loc, value.getType(), 0);
374 auto isOneHot0 = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::eq,
375 anded, zero, false);
376 auto isNotZero = comb::ICmpOp::create(builder, loc, comb::ICmpPredicate::ne,
377 value, zero, false);
378 auto result = comb::AndOp::create(builder, loc, isOneHot0, isNotZero);
379 return castToMoore(result);
380 }
381
382 default:
383 return Value{};
384 }
385}
386
387static Value getIsUnknown(OpBuilder &builder, Location loc, Value value,
388 moore::IntType valTy, MLIRContext *ctx) {
389 Value bitVal = value;
390 if (valTy.getWidth() > 1) {
391 auto mooreI1Type = moore::IntType::get(ctx, 1, valTy.getDomain());
392 bitVal = moore::ReduceXorOp::create(builder, loc, mooreI1Type, value);
393 }
394
395 auto xType = moore::IntType::get(ctx, 1, moore::Domain::FourValued);
396 auto xValue = FVInt::getAllX(1);
397 auto xConst = moore::ConstantOp::create(builder, loc, xType, xValue);
398
399 return moore::CaseEqOp::create(builder, loc, bitVal, xConst).getResult();
400}
401
403 const slang::ast::CallExpression &expr,
404 const slang::ast::CallExpression::SystemCallInfo &info, Location loc) {
405
406 const slang::ast::TimingControl *clock = nullptr;
407 auto clockIt = assertionCallClocks.find(&expr);
408 if (clockIt != assertionCallClocks.end())
409 clock = clockIt->second;
410
411 Value clockVal;
412 if (clock) {
413 const slang::ast::SignalEventControl *signal = nullptr;
414 if (clock->kind == slang::ast::TimingControlKind::SignalEvent) {
415 signal = &clock->as<slang::ast::SignalEventControl>();
416 } else if (clock->kind == slang::ast::TimingControlKind::EventList) {
417 mlir::emitError(loc, "sampled value functions with multiple event "
418 "triggers are not supported");
419 return {};
420 } else {
421 llvm_unreachable("unexpected clock kind for assertion");
422 }
423
424 if (signal->edge != slang::ast::EdgeKind::PosEdge) {
425 mlir::emitError(
426 loc,
427 "sampled value functions are only supported with posedge clocks");
428 return {};
429 }
430 clockVal = convertRvalueExpression(signal->expr);
431 if (clockVal)
432 clockVal = convertToI1(clockVal);
433 if (!clockVal)
434 return {};
435 }
436
437 const auto &subroutine = *info.subroutine;
438 auto args = expr.arguments();
439
440 FailureOr<Value> result;
441 Value value;
442 Value intVal;
443 Type originalType;
444 moore::IntType valTy;
445
446 switch (args.size()) {
447 case (1):
448 value = this->convertRvalueExpression(*args[0]);
449 originalType = value.getType();
450 valTy = dyn_cast<moore::IntType>(value.getType());
451 if (!valTy) {
452 mlir::emitError(loc) << "expected integer argument for `"
453 << subroutine.name << "`";
454 return {};
455 }
456
457 // IsUnknown is handled here rather than below with the others as below it
458 // would have already been converted to an integer type rather than bool
459 // type as here.
460 if (subroutine.knownNameId == slang::parsing::KnownSystemName::IsUnknown) {
461 return getIsUnknown(builder, loc, value, valTy, getContext());
462 }
463
464 // OneHot/OneHot0 are handled both here and below.
465 if ((subroutine.knownNameId == slang::parsing::KnownSystemName::OneHot ||
466 subroutine.knownNameId == slang::parsing::KnownSystemName::OneHot0) &&
467 (valTy.getDomain() == Domain::FourValued)) {
468 // In SystemVerilog, these system only returns 1b1 if the expression is
469 // fully known and the condition is met. So if any x or y bits, then
470 // these must return 1'b0.
471
472 // Detect if input contain unknown bits.
473 Value isUnknownMoore =
474 getIsUnknown(builder, loc, value, valTy, getContext());
475 Value isUnknown =
476 builder.createOrFold<moore::ToBuiltinIntOp>(loc, isUnknownMoore);
477
478 Value coerced = builder.createOrFold<moore::LogicToIntOp>(loc, value);
479 Value state2IntVal =
480 builder.createOrFold<moore::ToBuiltinIntOp>(loc, coerced);
481 if (!state2IntVal)
482 return {};
483
484 auto systemResult = this->convertAssertionSystemCallArity1(
485 subroutine, loc, state2IntVal, originalType, clockVal);
486 if (failed(systemResult) || !*systemResult)
487 return {};
488 Value onehot2state = *systemResult;
489
490 // Squash to 0 if unknown bits exist.
491 Value onehot2stateBuiltin = convertToI1(onehot2state);
492 Value zeroBuiltin =
493 hw::ConstantOp::create(builder, loc, builder.getI1Type(), 0);
494 Value resultBuiltin = comb::MuxOp::create(
495 builder, loc, isUnknown, zeroBuiltin, onehot2stateBuiltin);
496
497 Value finalResult =
498 moore::FromBuiltinIntOp::create(builder, loc, resultBuiltin);
499 return moore::IntToLogicOp::create(builder, loc, finalResult);
500 }
501
502 // If the value is four-valued, we need to map it to two-valued before we
503 // cast it to a builtin int
504 if (valTy.getDomain() == Domain::FourValued) {
505 value = builder.createOrFold<moore::LogicToIntOp>(loc, value);
506 }
507 intVal = builder.createOrFold<moore::ToBuiltinIntOp>(loc, value);
508 if (!intVal)
509 return {};
510 result = this->convertAssertionSystemCallArity1(subroutine, loc, intVal,
511 originalType, clockVal);
512 break;
513
514 default:
515 break;
516 }
517
518 if (failed(result))
519 return {};
520 if (*result)
521 return *result;
522
523 mlir::emitError(loc) << "unsupported system call `" << subroutine.name << "`";
524 return {};
525}
526
527Value Context::convertAssertionExpression(const slang::ast::AssertionExpr &expr,
528 Location loc) {
529 AssertionExprVisitor visitor{*this, loc};
530 return expr.visit(visitor);
531}
532// NOLINTEND(misc-no-recursion)
533
534/// Helper function to convert a value to an i1 value.
535Value Context::convertToI1(Value value) {
536 if (!value)
537 return {};
538 auto loc = value.getLoc();
539 auto type = dyn_cast<moore::IntType>(value.getType());
540 if (!type || type.getBitSize() != 1) {
541 mlir::emitError(loc, "expected a 1-bit integer");
542 return {};
543 }
544 if (type.getDomain() == Domain::FourValued) {
545 value = moore::LogicToIntOp::create(builder, loc, value);
546 }
547 return moore::ToBuiltinIntOp::create(builder, loc, value);
548}
549
550namespace {
551struct AssertionClockVisitor
552 : slang::ast::ASTVisitor<AssertionClockVisitor, true, true> {
554 const slang::analysis::AnalyzedAssertion &assertion;
555 const slang::ast::TimingControl *currentClock = nullptr;
556
557 AssertionClockVisitor(Context &context,
558 const slang::analysis::AnalyzedAssertion &assertion)
559 : context(context), assertion(assertion) {}
560
561 void handle(const slang::ast::CallExpression &node) {
562 if (currentClock)
563 context.assertionCallClocks[&node] = currentClock;
564 visitDefault(node);
565 }
566
567 template <typename T>
568 std::enable_if_t<std::is_base_of_v<slang::ast::AssertionExpr, T>>
569 handle(const T &node) {
570 auto *prevClock = currentClock;
571 if (auto *clk = assertion.getClock(node))
572 currentClock = clk;
573 visitDefault(node);
574 currentClock = prevClock;
575 }
576};
577} // namespace
578
580 compilation.freeze();
581 slang::analysis::AnalysisManager am;
582 am.addListener([this](const slang::analysis::AnalyzedAssertion &assertion) {
583 AssertionClockVisitor visitor{*this, assertion};
584 assertion.getRoot().visit(visitor);
585 });
586 am.analyze(compilation);
587 compilation.unfreeze();
588}
static Value getIsUnknown(OpBuilder &builder, Location loc, Value value, moore::IntType valTy, MLIRContext *ctx)
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
Definition FVInt.h:75
create(data_type, value)
Definition hw.py:433
@ FourValued
Four-valued types such as logic or integer.
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.
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
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)
MLIRContext * getContext()
Return the MLIR context.
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.