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