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