CIRCT 21.0.0git
Loading...
Searching...
No Matches
LowerDPI.cpp
Go to the documentation of this file.
1//===- LowerDPI.cpp - Lower to DPI to Sim dialects ------------------------===//
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//
9// This file defines the LowerDPI pass.
10//
11//===----------------------------------------------------------------------===//
12
19#include "mlir/IR/BuiltinOps.h"
20#include "mlir/IR/Threading.h"
21#include "mlir/Pass/Pass.h"
22#include "mlir/Support/LogicalResult.h"
23#include "llvm/ADT/MapVector.h"
24
25namespace circt {
26namespace firrtl {
27#define GEN_PASS_DEF_LOWERDPI
28#include "circt/Dialect/FIRRTL/Passes.h.inc"
29} // namespace firrtl
30} // namespace circt
31
32using namespace mlir;
33using namespace llvm;
34using namespace circt;
35using namespace circt::firrtl;
36
37namespace {
38struct LowerDPIPass : public circt::firrtl::impl::LowerDPIBase<LowerDPIPass> {
39 void runOnOperation() override;
40};
41
42// A helper struct to lower DPI intrinsics in the circuit.
43struct LowerDPI {
44 LowerDPI(CircuitOp circuitOp) : circuitOp(circuitOp), nameSpace(circuitOp) {}
45 // Tte main logic.
46 LogicalResult run();
47 bool changed() const { return !funcNameToCallSites.empty(); }
48
49private:
50 // Walk all modules and peel `funcNameToCallSites`.
51 void collectIntrinsics();
52
53 // Lower intrinsics recorded in `funcNameToCallSites`.
54 LogicalResult lower();
55
56 sim::DPIFuncOp getOrCreateDPIFuncDecl(DPICallIntrinsicOp op);
57 LogicalResult lowerDPIIntrinsic(DPICallIntrinsicOp op);
58
59 MapVector<StringAttr, SmallVector<DPICallIntrinsicOp>> funcNameToCallSites;
60
61 // A map stores DPI func op for its function name and type.
62 llvm::DenseMap<std::pair<StringAttr, Type>, sim::DPIFuncOp>
63 functionSignatureToDPIFuncOp;
64
65 firrtl::CircuitOp circuitOp;
66 CircuitNamespace nameSpace;
67};
68} // namespace
69
70void LowerDPI::collectIntrinsics() {
71 // A helper struct to collect DPI calls in the circuit.
72 struct DpiCallCollections {
73 FModuleOp module;
74 SmallVector<DPICallIntrinsicOp> dpiOps;
75 };
76
77 SmallVector<DpiCallCollections, 0> collections;
78 collections.reserve(64);
79
80 for (auto module : circuitOp.getOps<FModuleOp>())
81 collections.push_back(DpiCallCollections{module, {}});
82
83 parallelForEach(circuitOp.getContext(), collections, [](auto &result) {
84 result.module.walk(
85 [&](DPICallIntrinsicOp dpi) { result.dpiOps.push_back(dpi); });
86 });
87
88 for (auto &collection : collections)
89 for (auto dpi : collection.dpiOps)
90 funcNameToCallSites[dpi.getFunctionNameAttr()].push_back(dpi);
91}
92
93// Lower FIRRTL type to core dialect types in which array type is replaced with
94// an open array type.
95static Type lowerDPIArgumentType(Type type) {
96 auto loweredType = lowerType(type);
97 return loweredType.replace([](hw::ArrayType array) {
98 return sv::UnpackedOpenArrayType::get(array.getElementType());
99 });
100}
101
102static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder,
103 Value loweredValue) {
104 if (isa<IntegerType>(loweredValue.getType()))
105 return loweredValue;
106
107 auto array = dyn_cast<hw::ArrayType>(loweredValue.getType());
108 if (!array) {
109 // TODO: Support bundle or enum types.
110 return {};
111 }
112
113 SmallVector<Value> values;
114 auto length = array.getNumElements();
115 auto width = llvm::Log2_64_Ceil(length);
116
117 // Create an unpacked array from a packed array.
118 for (int i = length - 1; i >= 0; --i) {
119 auto index = builder.create<hw::ConstantOp>(APInt(width, i));
120 auto elem = convertToUnpackedArray(
121 builder, builder.create<hw::ArrayGetOp>(loweredValue, index));
122 if (!elem)
123 return {};
124 values.push_back(elem);
125 }
126
127 return builder.create<sv::UnpackedArrayCreateOp>(
128 hw::UnpackedArrayType::get(lowerType(array.getElementType()), length),
129 values);
130}
131
132static Value getLowered(ImplicitLocOpBuilder &builder, Value value) {
133 // Insert an unrealized conversion to cast FIRRTL type to HW type.
134 if (!value)
135 return value;
136
137 auto type = lowerType(value.getType());
138 Value result = builder.create<mlir::UnrealizedConversionCastOp>(type, value)
139 ->getResult(0);
140
141 // We may need to cast the lowered value to a DPI specific type (e.g. open
142 // array). Get a DPI type and check if they are same.
143 auto dpiType = lowerDPIArgumentType(value.getType());
144 if (type == dpiType)
145 return result;
146
147 // Cast into unpacked open array type.
148 result = convertToUnpackedArray(builder, result);
149 if (!result) {
150 mlir::emitError(value.getLoc())
151 << "contains a type that currently not supported";
152 return {};
153 }
154
155 return builder.create<sv::UnpackedOpenArrayCastOp>(dpiType, result);
156}
157
158LogicalResult LowerDPI::lower() {
159 for (auto [name, calls] : funcNameToCallSites) {
160 auto firstDPICallop = calls.front();
161 // Construct DPI func op.
162 auto firstDPIDecl = getOrCreateDPIFuncDecl(firstDPICallop);
163
164 auto inputTypes = firstDPICallop.getInputs().getTypes();
165 auto outputTypes = firstDPICallop.getResultTypes();
166
167 ImplicitLocOpBuilder builder(firstDPICallop.getLoc(),
168 circuitOp.getOperation());
169 auto lowerCall = [&](DPICallIntrinsicOp dpiOp) {
170 builder.setInsertionPoint(dpiOp);
171 auto clock = getLowered(builder, dpiOp.getClock());
172 auto enable = getLowered(builder, dpiOp.getEnable());
173 SmallVector<Value, 4> inputs;
174 inputs.reserve(dpiOp.getInputs().size());
175 for (auto input : dpiOp.getInputs()) {
176 inputs.push_back(getLowered(builder, input));
177 if (!inputs.back())
178 return failure();
179 }
180
181 SmallVector<Type> outputTypes;
182 if (dpiOp.getResult())
183 outputTypes.push_back(
184 lowerDPIArgumentType(dpiOp.getResult().getType()));
185
186 auto call = builder.create<sim::DPICallOp>(
187 outputTypes, firstDPIDecl.getSymNameAttr(), clock, enable, inputs);
188 if (!call.getResults().empty()) {
189 // Insert unrealized conversion cast HW type to FIRRTL type.
190 auto result = builder
191 .create<mlir::UnrealizedConversionCastOp>(
192 dpiOp.getResult().getType(), call.getResult(0))
193 ->getResult(0);
194 dpiOp.getResult().replaceAllUsesWith(result);
195 }
196 return success();
197 };
198
199 if (failed(lowerCall(firstDPICallop)))
200 return failure();
201
202 for (auto dpiOp : llvm::ArrayRef(calls).drop_front()) {
203 // Check that all DPI declaration match.
204 // TODO: This should be implemented as a verifier once function is added
205 // to FIRRTL.
206 if (dpiOp.getInputs().getTypes() != inputTypes) {
207 auto diag = firstDPICallop.emitOpError()
208 << "DPI function " << firstDPICallop.getFunctionNameAttr()
209 << " input types don't match ";
210 diag.attachNote(dpiOp.getLoc()) << " mismatched caller is here";
211 return failure();
212 }
213
214 if (dpiOp.getResultTypes() != outputTypes) {
215 auto diag = firstDPICallop.emitOpError()
216 << "DPI function " << firstDPICallop.getFunctionNameAttr()
217 << " output types don't match";
218 diag.attachNote(dpiOp.getLoc()) << " mismatched caller is here";
219 return failure();
220 }
221
222 if (failed(lowerCall(dpiOp)))
223 return failure();
224 }
225
226 for (auto callOp : calls)
227 callOp.erase();
228 }
229
230 return success();
231}
232
233sim::DPIFuncOp LowerDPI::getOrCreateDPIFuncDecl(DPICallIntrinsicOp op) {
234 ImplicitLocOpBuilder builder(op.getLoc(), circuitOp.getOperation());
235 builder.setInsertionPointToStart(circuitOp.getBodyBlock());
236 auto inputTypes = op.getInputs().getTypes();
237 auto outputTypes = op.getResultTypes();
238 ArrayAttr inputNames = op.getInputNamesAttr();
239 StringAttr outputName = op.getOutputNameAttr();
240 assert(outputTypes.size() <= 1);
241
242 SmallVector<hw::ModulePort> ports;
243 ports.reserve(inputTypes.size() + outputTypes.size());
244
245 // Add input arguments.
246 for (auto [idx, inType] : llvm::enumerate(inputTypes)) {
247 hw::ModulePort port;
248 port.dir = hw::ModulePort::Direction::Input;
249 port.name = inputNames ? cast<StringAttr>(inputNames[idx])
250 : builder.getStringAttr(Twine("in_") + Twine(idx));
251 port.type = lowerDPIArgumentType(inType);
252 ports.push_back(port);
253 }
254
255 // Add output arguments.
256 for (auto [idx, outType] : llvm::enumerate(outputTypes)) {
257 hw::ModulePort port;
258 port.dir = hw::ModulePort::Direction::Output;
259 port.name = outputName ? outputName
260 : builder.getStringAttr(Twine("out_") + Twine(idx));
261 port.type = lowerDPIArgumentType(outType);
262 ports.push_back(port);
263 }
264
265 auto modType = hw::ModuleType::get(builder.getContext(), ports);
266 auto it =
267 functionSignatureToDPIFuncOp.find({op.getFunctionNameAttr(), modType});
268 if (it != functionSignatureToDPIFuncOp.end())
269 return it->second;
270
271 auto funcSymbol = nameSpace.newName(op.getFunctionNameAttr().getValue());
272 auto funcOp = builder.create<sim::DPIFuncOp>(
273 funcSymbol, modType, ArrayAttr(), ArrayAttr(), op.getFunctionNameAttr());
274 // External function must have a private linkage.
275 funcOp.setPrivate();
276 functionSignatureToDPIFuncOp[{op.getFunctionNameAttr(), modType}] = funcOp;
277 return funcOp;
278}
279
280LogicalResult LowerDPI::run() {
281 collectIntrinsics();
282 return lower();
283}
284
285void LowerDPIPass::runOnOperation() {
286 auto circuitOp = getOperation();
287 LowerDPI lowerDPI(circuitOp);
288 if (failed(lowerDPI.run()))
289 return signalPassFailure();
290 if (!lowerDPI.changed())
291 return markAllAnalysesPreserved();
292}
293
294std::unique_ptr<mlir::Pass> circt::firrtl::createLowerDPIPass() {
295 return std::make_unique<LowerDPIPass>();
296}
assert(baseType &&"element must be base type")
static Value getLowered(ImplicitLocOpBuilder &builder, Value value)
Definition LowerDPI.cpp:132
static Type lowerDPIArgumentType(Type type)
Definition LowerDPI.cpp:95
static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder, Value loweredValue)
Definition LowerDPI.cpp:102
std::unique_ptr< mlir::Pass > createLowerDPIPass()
Definition LowerDPI.cpp:294
Type lowerType(Type type, std::optional< Location > loc={}, llvm::function_ref< hw::TypeAliasType(Type, BaseTypeAliasType, Location)> getTypeDeclFn={})
Given a type, return the corresponding lowered type for the HW dialect.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
int run(Type[Generator] generator=CppGenerator, cmdline_args=sys.argv)
Definition codegen.py:121
The namespace of a CircuitOp, generally inhabited by modules.
Definition Namespace.h:24
mlir::Type type
Definition HWTypes.h:31
mlir::StringAttr name
Definition HWTypes.h:30