CIRCT  20.0.0git
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 
13 using namespace circt;
14 using namespace ImportVerilog;
15 
16 static 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)
32 namespace {
33 
34 // Handle any of the event control constructs.
35 struct 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.
77 struct 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`.
97 static 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 
150 LogicalResult
151 Context::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)
const char * toString(Flow flow)
Definition: FIRRTLOps.cpp:197
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.
Definition: DebugAnalysis.h:21
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)
Definition: Statements.cpp:695
Location convertLocation(slang::SourceLocation loc)
Convert a slang SourceLocation into an MLIR Location.