CIRCT 20.0.0git
Loading...
Searching...
No Matches
SystemCLowerInstanceInterop.cpp
Go to the documentation of this file.
1//===- SystemCLowerInstanceInterop.cpp - Instance-side interop lowering ---===//
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 is the main SystemC instance-side interp lowering pass implementation.
10//
11//===----------------------------------------------------------------------===//
12
16#include "mlir/Dialect/Func/IR/FuncOps.h"
17#include "mlir/Pass/Pass.h"
18#include "mlir/Transforms/DialectConversion.h"
19
20namespace circt {
21namespace systemc {
22#define GEN_PASS_DEF_SYSTEMCLOWERINSTANCEINTEROP
23#include "circt/Dialect/SystemC/Passes.h.inc"
24} // namespace systemc
25} // namespace circt
26
27using namespace mlir;
28using namespace circt;
29using namespace circt::systemc;
30
31//===----------------------------------------------------------------------===//
32// Interop lowering patterns
33//===----------------------------------------------------------------------===//
34
35namespace {
36/// Lower the systemc::InteropVerilatedOp operation.
37class InteropVerilatedOpConversion
38 : public OpConversionPattern<InteropVerilatedOp> {
39public:
40 using OpConversionPattern<InteropVerilatedOp>::OpConversionPattern;
41
42 LogicalResult
43 matchAndRewrite(InteropVerilatedOp op, OpAdaptor adaptor,
44 ConversionPatternRewriter &rewriter) const override {
45 // TODO: instead of hardcoding the verilated module's class name, it should
46 // be derived from a configs attribute as this can be specified via the CLI
47 // arguments of verilator
48 // stateType ::= VModuleName*
49 SmallString<128> verilatedModuleName("V");
50 verilatedModuleName += op.getModuleName();
51 auto stateType = emitc::PointerType::get(
52 emitc::OpaqueType::get(op->getContext(), verilatedModuleName));
53 Location loc = op.getLoc();
54
55 // Include the C++ header produced by Verilator at the location of the HW
56 // module.
57 auto *hwModule =
58 SymbolTable::lookupNearestSymbolFrom(op, op.getModuleNameAttr());
59 OpBuilder includeBuilder(hwModule);
60 includeBuilder.create<emitc::IncludeOp>(
61 loc, (verilatedModuleName + ".h").str(), false);
62
63 // Request a pointer to the verilated module as persistent state.
64 Value state = rewriter
65 .create<interop::ProceduralAllocOp>(loc, stateType,
66 InteropMechanism::CPP)
67 .getStates()[0];
68
69 insertStateInitialization(rewriter, loc, state);
70
71 ValueRange results = insertUpdateLogic(
72 rewriter, loc, state, adaptor.getInputs(), op.getResults(),
73 adaptor.getInputNames(), adaptor.getResultNames());
74
75 insertStateDeallocation(rewriter, loc, state);
76
77 // Replace the return values of the instance with the result values of the
78 // interop update operation.
79 rewriter.replaceOp(op, results);
80 return success();
81 }
82
83private:
84 /// Insert a interop init operation to allocate an instance of the verilated
85 /// module on the heap and let the above requested pointer point to it.
86 void insertStateInitialization(PatternRewriter &rewriter, Location loc,
87 Value state) const {
88 auto initOp = rewriter.create<interop::ProceduralInitOp>(
89 loc, state, InteropMechanism::CPP);
90
91 OpBuilder initBuilder = OpBuilder::atBlockBegin(initOp.getBody());
92 Value newState =
93 initBuilder.create<NewOp>(loc, state.getType(), ValueRange());
94 initBuilder.create<interop::ReturnOp>(loc, newState);
95 }
96
97 /// Create an update interop operation to assign the input values to the input
98 /// ports of the verilated module, call 'eval', and read the output ports of
99 /// the verilated module.
100 ValueRange insertUpdateLogic(PatternRewriter &rewriter, Location loc,
101 Value stateValue, ValueRange inputValues,
102 ValueRange resultValues, ArrayAttr inputNames,
103 ArrayAttr resultNames) const {
104 auto updateOp = rewriter.create<interop::ProceduralUpdateOp>(
105 loc, resultValues.getTypes(), inputValues, stateValue,
106 InteropMechanism::CPP);
107
108 OpBuilder updateBuilder = OpBuilder::atBlockBegin(updateOp.getBody());
109
110 // Write to the verilated module's input ports.
111 Value state = updateOp.getBody()->getArguments().front();
112 for (size_t i = 0; i < inputValues.size(); ++i) {
113 Value member = updateBuilder.create<MemberAccessOp>(
114 loc, inputValues[i].getType(), state, cast<StringAttr>(inputNames[i]),
115 MemberAccessKind::Arrow);
116 updateBuilder.create<AssignOp>(loc, member,
117 updateOp.getBody()->getArgument(i + 1));
118 }
119
120 // Call 'eval'.
121 auto evalFunc = updateBuilder.create<MemberAccessOp>(
122 loc, FunctionType::get(updateBuilder.getContext(), {}, {}), state,
123 "eval", MemberAccessKind::Arrow);
124
125 // TODO: this has to be changed to a systemc::CallIndirectOp once the PR is
126 // merged, also remove the dependency to the func dialect from the cmake,
127 // header include, pass dependent dialects
128 updateBuilder.create<func::CallIndirectOp>(loc, evalFunc.getResult());
129
130 // Read the verilated module's output ports.
131 SmallVector<Value> results;
132 for (size_t i = 0; i < resultValues.size(); ++i) {
133 results.push_back(updateBuilder.create<MemberAccessOp>(
134 loc, resultValues[i].getType(), state,
135 cast<StringAttr>(resultNames[i]).getValue(),
136 MemberAccessKind::Arrow));
137 }
138
139 updateBuilder.create<interop::ReturnOp>(loc, results);
140
141 return updateOp->getResults();
142 }
143
144 /// Deallocate the memory allocated in the interop init operation.
145 void insertStateDeallocation(PatternRewriter &rewriter, Location loc,
146 Value state) const {
147 auto deallocOp = rewriter.create<interop::ProceduralDeallocOp>(
148 loc, state, InteropMechanism::CPP);
149
150 OpBuilder deallocBuilder = OpBuilder::atBlockBegin(deallocOp.getBody());
151 deallocBuilder.create<DeleteOp>(loc, deallocOp.getBody()->getArgument(0));
152 }
153};
154} // namespace
155
156//===----------------------------------------------------------------------===//
157// Pass initialization
158//===----------------------------------------------------------------------===//
159
160namespace {
161struct SystemCLowerInstanceInteropPass
162 : circt::systemc::impl::SystemCLowerInstanceInteropBase<
163 SystemCLowerInstanceInteropPass> {
164 void runOnOperation() override;
165};
166} // namespace
167
169 RewritePatternSet &patterns, MLIRContext *ctx) {
170 patterns.add<InteropVerilatedOpConversion>(ctx);
171}
172
173void SystemCLowerInstanceInteropPass::runOnOperation() {
174 RewritePatternSet patterns(&getContext());
175
176 ConversionTarget target(getContext());
177 target.addLegalDialect<interop::InteropDialect>();
178 target.addLegalDialect<emitc::EmitCDialect>();
179 target.addLegalDialect<SystemCDialect>();
180 target.addLegalOp<func::CallIndirectOp>();
181 target.addIllegalOp<InteropVerilatedOp>();
182
183 // Setup the conversion.
185
186 // Apply the partial conversion.
187 if (failed(
188 applyPartialConversion(getOperation(), target, std::move(patterns))))
189 signalPassFailure();
190}
191
192/// Create the SystemC Lower Interop pass.
194 return std::make_unique<SystemCLowerInstanceInteropPass>();
195}
std::unique_ptr< mlir::Pass > createSystemCLowerInstanceInteropPass()
Create the SystemC Lower Interop pass.
void populateSystemCLowerInstanceInteropPatterns(mlir::RewritePatternSet &patterns, mlir::MLIRContext *ctx)
Populate the rewrite patterns for SystemC's instance-side interop lowerings.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.