CIRCT  20.0.0git
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 
20 namespace circt {
21 namespace systemc {
22 #define GEN_PASS_DEF_SYSTEMCLOWERINSTANCEINTEROP
23 #include "circt/Dialect/SystemC/Passes.h.inc"
24 } // namespace systemc
25 } // namespace circt
26 
27 using namespace mlir;
28 using namespace circt;
29 using namespace circt::systemc;
30 
31 //===----------------------------------------------------------------------===//
32 // Interop lowering patterns
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 /// Lower the systemc::InteropVerilatedOp operation.
37 class InteropVerilatedOpConversion
38  : public OpConversionPattern<InteropVerilatedOp> {
39 public:
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 
83 private:
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 
160 namespace {
161 struct 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 
173 void 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 }
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
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.
Definition: DebugAnalysis.h:21