@@ -177,11 +177,159 @@ struct LowerLTLToCorePass
177177 : public circt::impl::LowerLTLToCoreBase<LowerLTLToCorePass> {
178178 LowerLTLToCorePass () = default ;
179179 void runOnOperation () override ;
180+
181+ private:
182+ int nameID = 0 ;
183+
184+ StringAttr nextName (OpBuilder &builder, StringRef prefix) {
185+ return builder.getStringAttr ((prefix + " _" + Twine (nameID++)).str ());
186+ }
187+
188+ Value createCompReg (OpBuilder &builder, Location loc, Value input, Value clk,
189+ StringRef prefix) {
190+ auto nameAttr = nextName (builder, prefix);
191+ auto i1 = builder.getI1Type ();
192+ Value initZero = seq::createConstantInitialValue (
193+ builder, loc, builder.getIntegerAttr (i1, 0 ));
194+ return seq::CompRegOp::create (builder, loc, input, clk,
195+ /* reset*/ Value (), /* rstValue*/ Value (),
196+ nameAttr, /* initialValue*/ initZero)
197+ .getResult ();
198+ }
199+
200+ Value shift (OpBuilder &builder, Location loc, Value input, Value clk,
201+ int64_t cycles, StringRef prefix) {
202+ Value cur = input;
203+ for (int64_t i = 0 ; i < cycles; ++i) {
204+ cur = createCompReg (builder, loc, cur, clk, prefix);
205+ }
206+ return cur;
207+ }
208+
209+ FailureOr<Value> getSeqClock (Value clkI1) {
210+ if (isa<seq::ClockType>(clkI1.getType ()))
211+ return clkI1;
212+ if (auto from = clkI1.getDefiningOp <seq::FromClockOp>())
213+ return from.getInput ();
214+ return failure ();
215+ }
216+
217+ FailureOr<Value> lowerImplicationWithDelay (OpBuilder &builder, Location loc,
218+ Value antecedent, ltl::DelayOp delay,
219+ Value clkI1) {
220+ if (!antecedent.getType ().isInteger (1 ))
221+ return failure ();
222+ if (!delay.getInput ().getType ().isInteger (1 ))
223+ return failure ();
224+
225+ int64_t start = static_cast <int64_t >(delay.getDelay ());
226+ if (start < 1 )
227+ return failure ();
228+
229+ auto lengthAttr = delay.getLength ();
230+ if (!lengthAttr)
231+ return failure ();
232+ int64_t length = static_cast <int64_t >(*lengthAttr);
233+ if (length < 0 )
234+ return failure ();
235+ int64_t end = start + length;
236+
237+ auto clkOr = getSeqClock (clkI1);
238+ if (failed (clkOr))
239+ return failure ();
240+ Value clk = *clkOr;
241+
242+ // Exact delay case: antecedent delayed by start cycles implies consequent now.
243+ if (length == 0 ) {
244+ Value delayed = shift (builder, loc, antecedent, clk, start, " ltl_delay" );
245+ Value notDelayed = comb::createOrFoldNot (loc, delayed, builder);
246+ auto orOp = comb::OrOp::create (builder, loc, ValueRange{notDelayed, delay.getInput ()}, false );
247+ return orOp.getResult ();
248+ }
249+
250+ // Bounded range case: if antecedent fires, consequent must hold at least
251+ // once in [start, end] cycles. Use a pending-request monitor that clears
252+ // when consequent is observed.
253+ Value notRight = comb::createOrFoldNot (loc, delay.getInput (), builder);
254+
255+ // Age 1 pending is the antecedent delayed by 1 cycle.
256+ Value pending = shift (builder, loc, antecedent, clk, 1 , " ltl_delay" );
257+ Value prev = pending;
258+
259+ for (int64_t age = 1 ; age <= end; ++age) {
260+ Value nextVal;
261+ if (age == 1 ) {
262+ nextVal = prev;
263+ } else if (age < start) {
264+ nextVal = prev;
265+ } else {
266+ auto andOp = comb::AndOp::create (builder, loc, ValueRange{prev, notRight}, false );
267+ nextVal = andOp.getResult ();
268+ }
269+ prev = createCompReg (builder, loc, nextVal, clk, " ltl_window" );
270+ }
271+
272+ // violation = pending_at_end AND NOT(consequent)
273+ auto viol = comb::AndOp::create (builder, loc, ValueRange{prev, notRight}, false );
274+ Value notViol = comb::createOrFoldNot (loc, viol.getResult (), builder);
275+ return notViol;
276+ }
277+
278+ FailureOr<Value> lowerClockedProperty (OpBuilder &builder, Location loc,
279+ Value prop, Value clkI1) {
280+ if (auto imp = prop.getDefiningOp <ltl::ImplicationOp>()) {
281+ auto delay = imp.getConsequent ().getDefiningOp <ltl::DelayOp>();
282+ if (!delay)
283+ return failure ();
284+ return lowerImplicationWithDelay (builder, loc, imp.getAntecedent (), delay,
285+ clkI1);
286+ }
287+ if (prop.getType ().isInteger (1 ))
288+ return prop;
289+ return failure ();
290+ }
180291};
181292} // namespace
182293
183294// Simply applies the conversion patterns defined above
184295void LowerLTLToCorePass::runOnOperation () {
296+ hw::HWModuleOp hwModule = getOperation ();
297+ OpBuilder builder (hwModule);
298+ bool failedLowering = false ;
299+
300+ auto lowerAssertLike = [&](Operation *op, Value prop) {
301+ auto clkOp = prop.getDefiningOp <ltl::ClockOp>();
302+ if (!clkOp)
303+ return ;
304+ OpBuilder::InsertionGuard guard (builder);
305+ builder.setInsertionPoint (op);
306+
307+ auto lowered = lowerClockedProperty (builder, op->getLoc (), clkOp.getInput (),
308+ clkOp.getClock ());
309+ if (failed (lowered)) {
310+ op->emitError (" unsupported LTL delay pattern for LowerLTLToCore" );
311+ failedLowering = true ;
312+ return ;
313+ }
314+ op->setOperand (0 , *lowered);
315+ if (clkOp->use_empty ())
316+ clkOp.erase ();
317+ };
318+
319+ hwModule.walk ([&](verif::AssertOp op) {
320+ if (failedLowering)
321+ return ;
322+ lowerAssertLike (op, op.getProperty ());
323+ });
324+
325+ hwModule.walk ([&](verif::AssumeOp op) {
326+ if (failedLowering)
327+ return ;
328+ lowerAssertLike (op, op.getProperty ());
329+ });
330+
331+ if (failedLowering)
332+ return signalPassFailure ();
185333
186334 // Set target dialects: We don't want to see any ltl or verif that might
187335 // come from an AssertProperty left in the result
0 commit comments