CIRCT 20.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 moore::Edge convertEdgeKind(const slang::ast::EdgeKind edge) {
17 using slang::ast::EdgeKind;
18 switch (edge) {
19 case EdgeKind::None:
20 return moore::Edge::AnyChange;
21 case EdgeKind::PosEdge:
22 return moore::Edge::PosEdge;
23 case EdgeKind::NegEdge:
24 return moore::Edge::NegEdge;
25 case EdgeKind::BothEdges:
26 return moore::Edge::BothEdges;
27 }
28 llvm_unreachable("all edge kinds handled");
29}
30
31// NOLINTBEGIN(misc-no-recursion)
32namespace {
33
34// Handle any of the event control constructs.
35struct EventControlVisitor {
36 Context &context;
37 Location loc;
38 OpBuilder &builder;
39
40 // Handle single signal events like `posedge x`, `negedge y iff z`, or `w`.
41 LogicalResult visit(const slang::ast::SignalEventControl &ctrl) {
42 auto edge = convertEdgeKind(ctrl.edge);
43 auto expr = context.convertRvalueExpression(ctrl.expr);
44 if (!expr)
45 return failure();
46 Value condition;
47 if (ctrl.iffCondition) {
48 condition = context.convertRvalueExpression(*ctrl.iffCondition);
49 condition = context.convertToBool(condition, Domain::TwoValued);
50 if (!condition)
51 return failure();
52 }
53 builder.create<moore::DetectEventOp>(loc, edge, expr, condition);
54 return success();
55 }
56
57 // Handle a list of signal events.
58 LogicalResult visit(const slang::ast::EventListControl &ctrl) {
59 for (const auto *event : ctrl.events) {
60 auto visitor = *this;
61 visitor.loc = context.convertLocation(event->sourceRange);
62 if (failed(event->visit(visitor)))
63 return failure();
64 }
65 return success();
66 }
67
68 // Emit an error for all other timing controls.
69 template <typename T>
70 LogicalResult visit(T &&ctrl) {
71 return mlir::emitError(loc)
72 << "unsupported event control: " << slang::ast::toString(ctrl.kind);
73 }
74};
75
76// Handle any of the delay control constructs.
77struct DelayControlVisitor {
78 Context &context;
79 Location loc;
80 OpBuilder &builder;
81
82 // Emit an error for all other timing controls.
83 template <typename T>
84 LogicalResult visit(T &&ctrl) {
85 return mlir::emitError(loc)
86 << "unsupported delay control: " << slang::ast::toString(ctrl.kind);
87 }
88};
89
90} // namespace
91
92// Entry point to timing control handling. This deals with the layer of repeats
93// that a timing control may be wrapped in, and also handles the implicit event
94// control which may appear at that point. For any event control a `WaitEventOp`
95// will be created and populated by `handleEventControl`. Any delay control will
96// be handled by `handleDelayControl`.
97static LogicalResult handleRoot(Context &context,
98 const slang::ast::TimingControl &ctrl,
99 moore::WaitEventOp &implicitWaitOp) {
100 auto &builder = context.builder;
101 auto loc = context.convertLocation(ctrl.sourceRange);
102
103 using slang::ast::TimingControlKind;
104 switch (ctrl.kind) {
105 // TODO: Actually implement a lowering for repeated event control. The main
106 // way to trigger this is through an intra-assignment timing control, which
107 // is not yet supported:
108 //
109 // a = repeat(3) @(posedge b) c;
110 //
111 // This will want to recursively call this function at the right insertion
112 // point to handle the timing control being repeated.
113 case TimingControlKind::RepeatedEvent:
114 return mlir::emitError(loc) << "unsupported repeated event control";
115
116 // Handle implicit events, i.e. `@*` and `@(*)`. This implicitly includes
117 // all variables read within the statement that follows after the event
118 // control. Since we haven't converted that statement yet, simply create and
119 // empty wait op and let `Context::convertTimingControl` populate it once
120 // the statement has been lowered.
121 case TimingControlKind::ImplicitEvent:
122 implicitWaitOp = builder.create<moore::WaitEventOp>(loc);
123 return success();
124
125 // Handle event control.
126 case TimingControlKind::SignalEvent:
127 case TimingControlKind::EventList: {
128 auto waitOp = builder.create<moore::WaitEventOp>(loc);
129 OpBuilder::InsertionGuard guard(builder);
130 builder.setInsertionPointToStart(&waitOp.getBody().emplaceBlock());
131 EventControlVisitor visitor{context, loc, builder};
132 return ctrl.visit(visitor);
133 }
134
135 // Handle delay control.
136 case TimingControlKind::Delay:
137 case TimingControlKind::Delay3:
138 case TimingControlKind::OneStepDelay:
139 case TimingControlKind::CycleDelay: {
140 DelayControlVisitor visitor{context, loc, builder};
141 return ctrl.visit(visitor);
142 }
143
144 default:
145 return mlir::emitError(loc, "unsupported timing control: ")
146 << slang::ast::toString(ctrl.kind);
147 }
148}
149
150LogicalResult
151Context::convertTimingControl(const slang::ast::TimingControl &ctrl,
152 const slang::ast::Statement &stmt) {
153 // Convert the timing control. Implicit event control will create a new empty
154 // `WaitEventOp` and assign it to `implicitWaitOp`. This op will be populated
155 // further down.
156 moore::WaitEventOp implicitWaitOp;
157 {
158 auto previousCallback = rvalueReadCallback;
159 auto done =
160 llvm::make_scope_exit([&] { rvalueReadCallback = previousCallback; });
161 // Reads happening as part of the event control should not be added to a
162 // surrounding implicit event control's list of implicitly observed
163 // variables.
164 rvalueReadCallback = nullptr;
165 if (failed(handleRoot(*this, ctrl, implicitWaitOp)))
166 return failure();
167 }
168
169 // Convert the statement. In case `implicitWaitOp` is set, we register a
170 // callback to collect all the variables read by the statement into
171 // `readValues`, such that we can populate the op with implicitly observed
172 // variables afterwards.
173 llvm::SmallSetVector<Value, 8> readValues;
174 {
175 auto previousCallback = rvalueReadCallback;
176 auto done =
177 llvm::make_scope_exit([&] { rvalueReadCallback = previousCallback; });
178 if (implicitWaitOp) {
179 rvalueReadCallback = [&](moore::ReadOp readOp) {
180 readValues.insert(readOp.getInput());
181 if (previousCallback)
182 previousCallback(readOp);
183 };
184 }
185 if (failed(convertStatement(stmt)))
186 return failure();
187 }
188
189 // Populate the implicit wait op with reads from the variables read by the
190 // statement.
191 if (implicitWaitOp) {
192 OpBuilder::InsertionGuard guard(builder);
193 builder.setInsertionPointToStart(&implicitWaitOp.getBody().emplaceBlock());
194 for (auto readValue : readValues) {
195 auto value =
196 builder.create<moore::ReadOp>(implicitWaitOp.getLoc(), readValue);
197 builder.create<moore::DetectEventOp>(
198 implicitWaitOp.getLoc(), moore::Edge::AnyChange, value, Value{});
199 }
200 }
201
202 return success();
203}
204// NOLINTEND(misc-no-recursion)
static moore::Edge convertEdgeKind(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.
LogicalResult convertTimingControl(const slang::ast::TimingControl &ctrl, const slang::ast::Statement &stmt)
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.