CIRCT 22.0.0git
Loading...
Searching...
No Matches
TimingControls.cpp
Go to the documentation of this file.
1//===- TimingControl.cpp - Slang timing control 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
10#include "slang/ast/TimingControl.h"
11#include "llvm/ADT/ScopeExit.h"
12
13using namespace circt;
14using namespace ImportVerilog;
15
16static ltl::ClockEdge convertEdgeKindLTL(const slang::ast::EdgeKind edge) {
17 using slang::ast::EdgeKind;
18 switch (edge) {
19 case EdgeKind::NegEdge:
20 return ltl::ClockEdge::Neg;
21 case EdgeKind::PosEdge:
22 return ltl::ClockEdge::Pos;
23 case EdgeKind::None:
24 // TODO: SV 16.16, what to do when no edge is specified?
25 // For now, assume all changes (two-valued should be the same as both
26 // edges)
27 case EdgeKind::BothEdges:
28 return ltl::ClockEdge::Both;
29 }
30 llvm_unreachable("all edge kinds handled");
31}
32
33static moore::Edge convertEdgeKind(const slang::ast::EdgeKind edge) {
34 using slang::ast::EdgeKind;
35 switch (edge) {
36 case EdgeKind::None:
37 return moore::Edge::AnyChange;
38 case EdgeKind::PosEdge:
39 return moore::Edge::PosEdge;
40 case EdgeKind::NegEdge:
41 return moore::Edge::NegEdge;
42 case EdgeKind::BothEdges:
43 return moore::Edge::BothEdges;
44 }
45 llvm_unreachable("all edge kinds handled");
46}
47
48// NOLINTBEGIN(misc-no-recursion)
49namespace {
50
51// Handle any of the event control constructs.
52struct EventControlVisitor {
53 Context &context;
54 Location loc;
55 OpBuilder &builder;
56
57 // Handle single signal events like `posedge x`, `negedge y iff z`, or `w`.
58 LogicalResult visit(const slang::ast::SignalEventControl &ctrl) {
59 auto edge = convertEdgeKind(ctrl.edge);
60 auto expr = context.convertRvalueExpression(ctrl.expr);
61 if (!expr)
62 return failure();
63 Value condition;
64 if (ctrl.iffCondition) {
65 condition = context.convertRvalueExpression(*ctrl.iffCondition);
66 condition = context.convertToBool(condition, Domain::TwoValued);
67 if (!condition)
68 return failure();
69 }
70 moore::DetectEventOp::create(builder, loc, edge, expr, condition);
71 return success();
72 }
73
74 // Handle a list of signal events.
75 LogicalResult visit(const slang::ast::EventListControl &ctrl) {
76 for (const auto *event : ctrl.events) {
77 auto visitor = *this;
78 visitor.loc = context.convertLocation(event->sourceRange);
79 if (failed(event->visit(visitor)))
80 return failure();
81 }
82 return success();
83 }
84
85 // Emit an error for all other timing controls.
86 template <typename T>
87 LogicalResult visit(T &&ctrl) {
88 return mlir::emitError(loc)
89 << "unsupported event control: " << slang::ast::toString(ctrl.kind);
90 }
91};
92
93// Handle any of the delay control constructs.
94struct DelayControlVisitor {
95 Context &context;
96 Location loc;
97 OpBuilder &builder;
98
99 // Handle delays.
100 LogicalResult visit(const slang::ast::DelayControl &ctrl) {
101 auto delay = context.convertRvalueExpression(
102 ctrl.expr, moore::TimeType::get(builder.getContext()));
103 if (!delay)
104 return failure();
105 moore::WaitDelayOp::create(builder, loc, delay);
106 return success();
107 }
108
109 // Emit an error for all other timing controls.
110 template <typename T>
111 LogicalResult visit(T &&ctrl) {
112 return mlir::emitError(loc)
113 << "unsupported delay control: " << slang::ast::toString(ctrl.kind);
114 }
115};
116
117struct LTLClockControlVisitor {
118 Context &context;
119 Location loc;
120 OpBuilder &builder;
121 Value seqOrPro;
122
123 Value visit(const slang::ast::SignalEventControl &ctrl) {
124 auto edge = convertEdgeKindLTL(ctrl.edge);
125 auto expr = context.convertRvalueExpression(ctrl.expr);
126 if (!expr)
127 return Value{};
128 Value condition;
129 if (ctrl.iffCondition) {
130 condition = context.convertRvalueExpression(*ctrl.iffCondition);
131 condition = context.convertToBool(condition, Domain::TwoValued);
132 if (!condition)
133 return Value{};
134 }
135 expr = context.convertToI1(expr);
136 if (!expr)
137 return Value{};
138 return ltl::ClockOp::create(builder, loc, seqOrPro, edge, expr);
139 }
140
141 template <typename T>
142 Value visit(T &&ctrl) {
143 mlir::emitError(loc, "unsupported LTL clock control: ")
144 << slang::ast::toString(ctrl.kind);
145 return Value{};
146 }
147};
148
149} // namespace
150
151// Entry point to timing control handling. This deals with the layer of repeats
152// that a timing control may be wrapped in, and also handles the implicit event
153// control which may appear at that point. For any event control a `WaitEventOp`
154// will be created and populated by `handleEventControl`. Any delay control will
155// be handled by `handleDelayControl`.
156static LogicalResult handleRoot(Context &context,
157 const slang::ast::TimingControl &ctrl,
158 moore::WaitEventOp &implicitWaitOp) {
159 auto &builder = context.builder;
160 auto loc = context.convertLocation(ctrl.sourceRange);
161
162 using slang::ast::TimingControlKind;
163 switch (ctrl.kind) {
164 // TODO: Actually implement a lowering for repeated event control. The main
165 // way to trigger this is through an intra-assignment timing control, which
166 // is not yet supported:
167 //
168 // a = repeat(3) @(posedge b) c;
169 //
170 // This will want to recursively call this function at the right insertion
171 // point to handle the timing control being repeated.
172 case TimingControlKind::RepeatedEvent:
173 return mlir::emitError(loc) << "unsupported repeated event control";
174
175 // Handle implicit events, i.e. `@*` and `@(*)`. This implicitly includes
176 // all variables read within the statement that follows after the event
177 // control. Since we haven't converted that statement yet, simply create and
178 // empty wait op and let `Context::convertTimingControl` populate it once
179 // the statement has been lowered.
180 case TimingControlKind::ImplicitEvent:
181 implicitWaitOp = moore::WaitEventOp::create(builder, loc);
182 return success();
183
184 // Handle event control.
185 case TimingControlKind::SignalEvent:
186 case TimingControlKind::EventList: {
187 auto waitOp = moore::WaitEventOp::create(builder, loc);
188 OpBuilder::InsertionGuard guard(builder);
189 builder.setInsertionPointToStart(&waitOp.getBody().emplaceBlock());
190 EventControlVisitor visitor{context, loc, builder};
191 return ctrl.visit(visitor);
192 }
193
194 // Handle delay control.
195 case TimingControlKind::Delay:
196 case TimingControlKind::Delay3:
197 case TimingControlKind::OneStepDelay:
198 case TimingControlKind::CycleDelay: {
199 DelayControlVisitor visitor{context, loc, builder};
200 return ctrl.visit(visitor);
201 }
202
203 default:
204 return mlir::emitError(loc, "unsupported timing control: ")
205 << slang::ast::toString(ctrl.kind);
206 }
207}
208
209LogicalResult
210Context::convertTimingControl(const slang::ast::TimingControl &ctrl,
211 const slang::ast::Statement &stmt) {
212 // Convert the timing control. Implicit event control will create a new empty
213 // `WaitEventOp` and assign it to `implicitWaitOp`. This op will be populated
214 // further down.
215 moore::WaitEventOp implicitWaitOp;
216 {
217 auto previousCallback = rvalueReadCallback;
218 auto done =
219 llvm::make_scope_exit([&] { rvalueReadCallback = previousCallback; });
220 // Reads happening as part of the event control should not be added to a
221 // surrounding implicit event control's list of implicitly observed
222 // variables.
223 rvalueReadCallback = nullptr;
224 if (failed(handleRoot(*this, ctrl, implicitWaitOp)))
225 return failure();
226 }
227
228 // Convert the statement. In case `implicitWaitOp` is set, we register a
229 // callback to collect all the variables read by the statement into
230 // `readValues`, such that we can populate the op with implicitly observed
231 // variables afterwards.
232 llvm::SmallSetVector<Value, 8> readValues;
233 {
234 auto previousCallback = rvalueReadCallback;
235 auto done =
236 llvm::make_scope_exit([&] { rvalueReadCallback = previousCallback; });
237 if (implicitWaitOp) {
238 rvalueReadCallback = [&](moore::ReadOp readOp) {
239 readValues.insert(readOp.getInput());
240 if (previousCallback)
241 previousCallback(readOp);
242 };
243 }
244 if (failed(convertStatement(stmt)))
245 return failure();
246 }
247
248 // Populate the implicit wait op with reads from the variables read by the
249 // statement.
250 if (implicitWaitOp) {
251 OpBuilder::InsertionGuard guard(builder);
252 builder.setInsertionPointToStart(&implicitWaitOp.getBody().emplaceBlock());
253 for (auto readValue : readValues) {
254 auto value =
255 moore::ReadOp::create(builder, implicitWaitOp.getLoc(), readValue);
256 moore::DetectEventOp::create(builder, implicitWaitOp.getLoc(),
257 moore::Edge::AnyChange, value, Value{});
258 }
259 }
260
261 return success();
262}
263
264Value Context::convertLTLTimingControl(const slang::ast::TimingControl &ctrl,
265 const Value &seqOrPro) {
266 auto &builder = this->builder;
267 auto loc = this->convertLocation(ctrl.sourceRange);
268 LTLClockControlVisitor visitor{*this, loc, builder, seqOrPro};
269 return ctrl.visit(visitor);
270}
271// NOLINTEND(misc-no-recursion)
static moore::Edge convertEdgeKind(const slang::ast::EdgeKind edge)
static ltl::ClockEdge convertEdgeKindLTL(const slang::ast::EdgeKind edge)
static LogicalResult handleRoot(Context &context, const slang::ast::TimingControl &ctrl, moore::WaitEventOp &implicitWaitOp)
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.
std::function< void(moore::ReadOp)> rvalueReadCallback
A listener called for every variable or net being read.
Value convertToBool(Value value)
Helper function to convert a value to its "truthy" boolean value.
Value convertRvalueExpression(const slang::ast::Expression &expr, Type requiredType={})
LogicalResult convertStatement(const slang::ast::Statement &stmt)
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.