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"
27 #define GEN_PASS_DEF_LOWERDPI
28 #include "circt/Dialect/FIRRTL/Passes.h.inc"
34 using namespace circt;
38 struct LowerDPIPass :
public circt::firrtl::impl::LowerDPIBase<LowerDPIPass> {
39 void runOnOperation()
override;
44 LowerDPI(CircuitOp circuitOp) : circuitOp(circuitOp), nameSpace(circuitOp) {}
47 bool changed()
const {
return !funcNameToCallSites.empty(); }
51 void collectIntrinsics();
54 LogicalResult lower();
56 sim::DPIFuncOp getOrCreateDPIFuncDecl(DPICallIntrinsicOp op);
57 LogicalResult lowerDPIIntrinsic(DPICallIntrinsicOp op);
59 MapVector<StringAttr, SmallVector<DPICallIntrinsicOp>> funcNameToCallSites;
62 llvm::DenseMap<std::pair<StringAttr, Type>, sim::DPIFuncOp>
63 functionSignatureToDPIFuncOp;
65 firrtl::CircuitOp circuitOp;
70 void LowerDPI::collectIntrinsics() {
72 struct DpiCallCollections {
74 SmallVector<DPICallIntrinsicOp> dpiOps;
77 SmallVector<DpiCallCollections, 0> collections;
78 collections.reserve(64);
80 for (
auto module : circuitOp.getOps<FModuleOp>())
81 collections.push_back(DpiCallCollections{module, {}});
83 parallelForEach(circuitOp.getContext(), collections, [](
auto &result) {
85 [&](DPICallIntrinsicOp dpi) { result.dpiOps.push_back(dpi); });
88 for (
auto &collection : collections)
89 for (
auto dpi : collection.dpiOps)
90 funcNameToCallSites[dpi.getFunctionNameAttr()].push_back(dpi);
97 return loweredType.replace([](hw::ArrayType array) {
103 Value loweredValue) {
104 if (isa<IntegerType>(loweredValue.getType()))
107 auto array = dyn_cast<hw::ArrayType>(loweredValue.getType());
113 SmallVector<Value> values;
114 auto length = array.getNumElements();
115 auto width = llvm::Log2_64_Ceil(length);
118 for (
int i = length - 1; i >= 0; --i) {
124 values.push_back(elem);
127 return builder.create<sv::UnpackedArrayCreateOp>(
132 static Value
getLowered(ImplicitLocOpBuilder &builder, Value value) {
138 Value result = builder.create<mlir::UnrealizedConversionCastOp>(type, value)
150 mlir::emitError(value.getLoc())
151 <<
"contains a type that currently not supported";
155 return builder.create<sv::UnpackedOpenArrayCastOp>(dpiType, result);
158 LogicalResult LowerDPI::lower() {
159 for (
auto [name, calls] : funcNameToCallSites) {
160 auto firstDPICallop = calls.front();
162 auto firstDPIDecl = getOrCreateDPIFuncDecl(firstDPICallop);
164 auto inputTypes = firstDPICallop.getInputs().getTypes();
165 auto outputTypes = firstDPICallop.getResultTypes();
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()) {
181 SmallVector<Type> outputTypes;
182 if (dpiOp.getResult())
183 outputTypes.push_back(
186 auto call = builder.create<sim::DPICallOp>(
187 outputTypes, firstDPIDecl.getSymNameAttr(), clock, enable, inputs);
188 if (!call.getResults().empty()) {
190 auto result = builder
191 .create<mlir::UnrealizedConversionCastOp>(
192 dpiOp.getResult().getType(), call.getResult(0))
194 dpiOp.getResult().replaceAllUsesWith(result);
199 if (failed(lowerCall(firstDPICallop)))
202 for (
auto dpiOp : llvm::ArrayRef(calls).drop_front()) {
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";
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";
222 if (failed(lowerCall(dpiOp)))
226 for (
auto callOp : calls)
233 sim::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);
242 SmallVector<hw::ModulePort> ports;
243 ports.reserve(inputTypes.size() + outputTypes.size());
246 for (
auto [idx, inType] : llvm::enumerate(inputTypes)) {
249 port.name = inputNames ? cast<StringAttr>(inputNames[idx])
250 : builder.getStringAttr(Twine(
"in_") + Twine(idx));
252 ports.push_back(port);
256 for (
auto [idx, outType] : llvm::enumerate(outputTypes)) {
259 port.name = outputName ? outputName
260 : builder.getStringAttr(Twine(
"out_") + Twine(idx));
262 ports.push_back(port);
267 functionSignatureToDPIFuncOp.find({op.getFunctionNameAttr(), modType});
268 if (it != functionSignatureToDPIFuncOp.end())
271 auto funcSymbol = nameSpace.newName(op.getFunctionNameAttr().getValue());
272 auto funcOp = builder.create<sim::DPIFuncOp>(
273 funcSymbol, modType, ArrayAttr(), ArrayAttr(), op.getFunctionNameAttr());
276 functionSignatureToDPIFuncOp[{op.getFunctionNameAttr(), modType}] = funcOp;
285 void 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();
295 return std::make_unique<LowerDPIPass>();
assert(baseType &&"element must be base type")
static Value getLowered(ImplicitLocOpBuilder &builder, Value value)
static Type lowerDPIArgumentType(Type type)
static Value convertToUnpackedArray(ImplicitLocOpBuilder &builder, Value loweredValue)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
std::unique_ptr< mlir::Pass > createLowerDPIPass()
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)
The namespace of a CircuitOp, generally inhabited by modules.