CIRCT 21.0.0git
Loading...
Searching...
No Matches
SimToSV.cpp
Go to the documentation of this file.
1//===- LowerSimToSV.cpp - Sim to SV 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 transform translates Sim ops to SV.
10//
11//===----------------------------------------------------------------------===//
12
22#include "mlir/IR/Builders.h"
23#include "mlir/IR/DialectImplementation.h"
24#include "mlir/IR/ImplicitLocOpBuilder.h"
25#include "mlir/IR/Threading.h"
26#include "mlir/Pass/Pass.h"
27#include "mlir/Transforms/DialectConversion.h"
28
29#define DEBUG_TYPE "lower-sim-to-sv"
30
31namespace circt {
32#define GEN_PASS_DEF_LOWERSIMTOSV
33#include "circt/Conversion/Passes.h.inc"
34} // namespace circt
35
36using namespace circt;
37using namespace sim;
38
39namespace {
40
41struct SimConversionState {
42 hw::HWModuleOp module;
43 bool usedSynthesisMacro = false;
44 SetVector<StringAttr> dpiCallees;
45};
46
47template <typename T>
48struct SimConversionPattern : public OpConversionPattern<T> {
49 explicit SimConversionPattern(MLIRContext *context, SimConversionState &state)
50 : OpConversionPattern<T>(context), state(state) {}
51
52 SimConversionState &state;
53};
54
55} // namespace
56
57// Lower `sim.plusargs.test` to a standard SV implementation.
58//
59class PlusArgsTestLowering : public SimConversionPattern<PlusArgsTestOp> {
60public:
61 using SimConversionPattern<PlusArgsTestOp>::SimConversionPattern;
62
63 LogicalResult
64 matchAndRewrite(PlusArgsTestOp op, OpAdaptor adaptor,
65 ConversionPatternRewriter &rewriter) const final {
66 auto loc = op.getLoc();
67 auto resultType = rewriter.getIntegerType(1);
68 auto str = rewriter.create<sv::ConstantStrOp>(loc, op.getFormatString());
69 auto reg = rewriter.create<sv::RegOp>(loc, resultType,
70 rewriter.getStringAttr("_pargs"));
71 rewriter.create<sv::InitialOp>(loc, [&] {
72 auto call = rewriter.create<sv::SystemFunctionOp>(
73 loc, resultType, "test$plusargs", ArrayRef<Value>{str});
74 rewriter.create<sv::BPAssignOp>(loc, reg, call);
75 });
76
77 rewriter.replaceOpWithNewOp<sv::ReadInOutOp>(op, reg);
78 return success();
79 }
80};
81
82// Lower `sim.plusargs.value` to a standard SV implementation.
83//
84class PlusArgsValueLowering : public SimConversionPattern<PlusArgsValueOp> {
85public:
86 using SimConversionPattern<PlusArgsValueOp>::SimConversionPattern;
87
88 LogicalResult
89 matchAndRewrite(PlusArgsValueOp op, OpAdaptor adaptor,
90 ConversionPatternRewriter &rewriter) const final {
91 auto loc = op.getLoc();
92
93 auto i1ty = rewriter.getIntegerType(1);
94 auto type = op.getResult().getType();
95
96 auto wirev = rewriter.create<sv::WireOp>(
97 loc, type, rewriter.getStringAttr("_pargs_v"));
98 auto wiref = rewriter.create<sv::WireOp>(
99 loc, i1ty, rewriter.getStringAttr("_pargs_f"));
100
101 state.usedSynthesisMacro = true;
102 rewriter.create<sv::IfDefOp>(
103 loc, "SYNTHESIS",
104 [&]() {
105 auto cstFalse = rewriter.create<hw::ConstantOp>(loc, APInt(1, 0));
106 auto cstZ = rewriter.create<sv::ConstantZOp>(loc, type);
107 auto assignZ = rewriter.create<sv::AssignOp>(loc, wirev, cstZ);
109 assignZ,
110 sv::SVAttributeAttr::get(
111 rewriter.getContext(),
112 "This dummy assignment exists to avoid undriven lint "
113 "warnings (e.g., Verilator UNDRIVEN).",
114 /*emitAsComment=*/true));
115 rewriter.create<sv::AssignOp>(loc, wiref, cstFalse);
116 },
117 [&]() {
118 auto i32ty = rewriter.getIntegerType(32);
119 auto regf = rewriter.create<sv::RegOp>(
120 loc, i32ty, rewriter.getStringAttr("_found"));
121 auto regv = rewriter.create<sv::RegOp>(
122 loc, type, rewriter.getStringAttr("_value"));
123 rewriter.create<sv::InitialOp>(loc, [&] {
124 auto str =
125 rewriter.create<sv::ConstantStrOp>(loc, op.getFormatString());
126 auto call = rewriter.create<sv::SystemFunctionOp>(
127 loc, i32ty, "value$plusargs", ArrayRef<Value>{str, regv});
128 rewriter.create<sv::BPAssignOp>(loc, regf, call);
129 });
130 Value readRegF = rewriter.create<sv::ReadInOutOp>(loc, regf);
131 Value readRegV = rewriter.create<sv::ReadInOutOp>(loc, regv);
132 auto cstTrue = rewriter.create<hw::ConstantOp>(loc, i32ty, 1);
133 // Squash any X coming from the regf to 0.
134 auto cmp = rewriter.create<comb::ICmpOp>(
135 loc, comb::ICmpPredicate::ceq, readRegF, cstTrue);
136 rewriter.create<sv::AssignOp>(loc, wiref, cmp);
137 rewriter.create<sv::AssignOp>(loc, wirev, readRegV);
138 });
139
140 Value readf = rewriter.create<sv::ReadInOutOp>(loc, wiref);
141 Value readv = rewriter.create<sv::ReadInOutOp>(loc, wirev);
142
143 rewriter.replaceOp(op, {readf, readv});
144 return success();
145 }
146};
147
148template <typename FromOp, typename ToOp>
149class SimulatorStopLowering : public SimConversionPattern<FromOp> {
150public:
151 using SimConversionPattern<FromOp>::SimConversionPattern;
152
153 LogicalResult
154 matchAndRewrite(FromOp op, typename FromOp::Adaptor adaptor,
155 ConversionPatternRewriter &rewriter) const final {
156 auto loc = op.getLoc();
157
158 Value clockCast = rewriter.create<seq::FromClockOp>(loc, adaptor.getClk());
159
160 this->state.usedSynthesisMacro = true;
161 rewriter.create<sv::IfDefOp>(
162 loc, "SYNTHESIS", [&] {},
163 [&] {
164 rewriter.create<sv::AlwaysOp>(
165 loc, sv::EventControl::AtPosEdge, clockCast, [&] {
166 rewriter.create<sv::IfOp>(loc, adaptor.getCond(),
167 [&] { rewriter.create<ToOp>(loc); });
168 });
169 });
170
171 rewriter.eraseOp(op);
172
173 return success();
174 }
175};
176
177class DPICallLowering : public SimConversionPattern<DPICallOp> {
178public:
179 using SimConversionPattern<DPICallOp>::SimConversionPattern;
180
181 LogicalResult
182 matchAndRewrite(DPICallOp op, OpAdaptor adaptor,
183 ConversionPatternRewriter &rewriter) const final {
184 auto loc = op.getLoc();
185 // Record the callee.
186 state.dpiCallees.insert(op.getCalleeAttr().getAttr());
187
188 bool isClockedCall = !!op.getClock();
189 bool hasEnable = !!op.getEnable();
190
191 SmallVector<sv::RegOp> temporaries;
192 SmallVector<Value> reads;
193 for (auto [type, result] :
194 llvm::zip(op.getResultTypes(), op.getResults())) {
195 temporaries.push_back(rewriter.create<sv::RegOp>(op.getLoc(), type));
196 reads.push_back(
197 rewriter.create<sv::ReadInOutOp>(op.getLoc(), temporaries.back()));
198 }
199
200 auto emitCall = [&]() {
201 auto call = rewriter.create<sv::FuncCallProceduralOp>(
202 op.getLoc(), op.getResultTypes(), op.getCalleeAttr(),
203 adaptor.getInputs());
204 for (auto [lhs, rhs] : llvm::zip(temporaries, call.getResults())) {
205 if (isClockedCall)
206 rewriter.create<sv::PAssignOp>(op.getLoc(), lhs, rhs);
207 else
208 rewriter.create<sv::BPAssignOp>(op.getLoc(), lhs, rhs);
209 }
210 };
211 if (isClockedCall) {
212 Value clockCast =
213 rewriter.create<seq::FromClockOp>(loc, adaptor.getClock());
214 rewriter.create<sv::AlwaysOp>(
215 loc, ArrayRef<sv::EventControl>{sv::EventControl::AtPosEdge},
216 ArrayRef<Value>{clockCast}, [&]() {
217 if (!hasEnable)
218 return emitCall();
219 rewriter.create<sv::IfOp>(op.getLoc(), adaptor.getEnable(),
220 emitCall);
221 });
222 } else {
223 // Unclocked call is lowered into always_comb.
224 // TODO: If there is a return value and no output argument, use an
225 // unclocked call op.
226 rewriter.create<sv::AlwaysCombOp>(loc, [&]() {
227 if (!hasEnable)
228 return emitCall();
229 auto assignXToResults = [&] {
230 for (auto lhs : temporaries) {
231 auto xValue = rewriter.create<sv::ConstantXOp>(
232 op.getLoc(), lhs.getType().getElementType());
233 rewriter.create<sv::BPAssignOp>(op.getLoc(), lhs, xValue);
234 }
235 };
236 rewriter.create<sv::IfOp>(op.getLoc(), adaptor.getEnable(), emitCall,
237 assignXToResults);
238 });
239 }
240
241 rewriter.replaceOp(op, reads);
242 return success();
243 }
244};
245
246// A helper struct to lower DPI function/call.
248 llvm::DenseMap<StringAttr, StringAttr> symbolToFragment;
250 LowerDPIFunc(mlir::ModuleOp module) { nameSpace.add(module); }
251 void lower(sim::DPIFuncOp func);
252 void addFragments(hw::HWModuleOp module,
253 ArrayRef<StringAttr> dpiCallees) const;
254};
255
256void LowerDPIFunc::lower(sim::DPIFuncOp func) {
257 ImplicitLocOpBuilder builder(func.getLoc(), func);
258 ArrayAttr inputLocsAttr, outputLocsAttr;
259 if (func.getArgumentLocs()) {
260 SmallVector<Attribute> inputLocs, outputLocs;
261 for (auto [port, loc] :
262 llvm::zip(func.getModuleType().getPorts(),
263 func.getArgumentLocsAttr().getAsRange<LocationAttr>())) {
264 (port.dir == hw::ModulePort::Output ? outputLocs : inputLocs)
265 .push_back(loc);
266 }
267 inputLocsAttr = builder.getArrayAttr(inputLocs);
268 outputLocsAttr = builder.getArrayAttr(outputLocs);
269 }
270
271 auto svFuncDecl =
272 builder.create<sv::FuncOp>(func.getSymNameAttr(), func.getModuleType(),
273 func.getPerArgumentAttrsAttr(), inputLocsAttr,
274 outputLocsAttr, func.getVerilogNameAttr());
275 // DPI function is a declaration so it must be a private function.
276 svFuncDecl.setPrivate();
277 auto name = builder.getStringAttr(nameSpace.newName(
278 func.getSymNameAttr().getValue(), "dpi_import_fragument"));
279
280 // Add include guards to avoid duplicate declarations. See Issue 7458.
281 auto macroDecl = builder.create<sv::MacroDeclOp>(nameSpace.newName(
282 "__CIRCT_DPI_IMPORT", func.getSymNameAttr().getValue().upper()));
283 builder.create<emit::FragmentOp>(name, [&]() {
284 builder.create<sv::IfDefOp>(
285 macroDecl.getSymNameAttr(), []() {},
286 [&]() {
287 builder.create<sv::FuncDPIImportOp>(func.getSymNameAttr(),
288 StringAttr());
289 builder.create<sv::MacroDefOp>(macroDecl.getSymNameAttr(), "");
290 });
291 });
292
293 symbolToFragment.insert({func.getSymNameAttr(), name});
294 func.erase();
295}
296
298 ArrayRef<StringAttr> dpiCallees) const {
299 llvm::SetVector<Attribute> fragments;
300 // Add existing emit fragments.
301 if (auto exstingFragments =
302 module->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName()))
303 for (auto fragment : exstingFragments.getAsRange<FlatSymbolRefAttr>())
304 fragments.insert(fragment);
305 for (auto callee : dpiCallees) {
306 auto attr = symbolToFragment.at(callee);
307 fragments.insert(FlatSymbolRefAttr::get(attr));
308 }
309 if (!fragments.empty())
310 module->setAttr(
311 emit::getFragmentsAttrName(),
312 ArrayAttr::get(module.getContext(), fragments.takeVector()));
313}
314
315namespace {
316struct SimToSVPass : public circt::impl::LowerSimToSVBase<SimToSVPass> {
317 void runOnOperation() override {
318 auto circuit = getOperation();
319 MLIRContext *context = &getContext();
320 LowerDPIFunc lowerDPIFunc(circuit);
321
322 // Lower DPI functions.
323 for (auto func :
324 llvm::make_early_inc_range(circuit.getOps<sim::DPIFuncOp>()))
325 lowerDPIFunc.lower(func);
326
327 std::atomic<bool> usedSynthesisMacro = false;
328 auto lowerModule = [&](hw::HWModuleOp module) {
329 SimConversionState state;
330 ConversionTarget target(*context);
331 target.addIllegalDialect<SimDialect>();
332 target.addLegalDialect<sv::SVDialect>();
333 target.addLegalDialect<hw::HWDialect>();
334 target.addLegalDialect<seq::SeqDialect>();
335 target.addLegalDialect<comb::CombDialect>();
336
337 RewritePatternSet patterns(context);
338 patterns.add<PlusArgsTestLowering>(context, state);
339 patterns.add<PlusArgsValueLowering>(context, state);
341 state);
343 state);
344 patterns.add<DPICallLowering>(context, state);
345 auto result = applyPartialConversion(module, target, std::move(patterns));
346
347 if (failed(result))
348 return result;
349
350 // Set the emit fragment.
351 lowerDPIFunc.addFragments(module, state.dpiCallees.takeVector());
352
353 if (state.usedSynthesisMacro)
354 usedSynthesisMacro = true;
355 return result;
356 };
357
358 if (failed(mlir::failableParallelForEach(
359 context, circuit.getOps<hw::HWModuleOp>(), lowerModule)))
360 return signalPassFailure();
361
362 if (usedSynthesisMacro) {
363 Operation *op = circuit.lookupSymbol("SYNTHESIS");
364 if (op) {
365 if (!isa<sv::MacroDeclOp>(op)) {
366 op->emitOpError("should be a macro declaration");
367 return signalPassFailure();
368 }
369 } else {
370 auto builder = ImplicitLocOpBuilder::atBlockBegin(
371 UnknownLoc::get(context), circuit.getBody());
372 builder.create<sv::MacroDeclOp>("SYNTHESIS");
373 }
374 }
375 }
376};
377} // anonymous namespace
378
379std::unique_ptr<Pass> circt::createLowerSimToSVPass() {
380 return std::make_unique<SimToSVPass>();
381}
LogicalResult matchAndRewrite(DPICallOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
Definition SimToSV.cpp:182
LogicalResult matchAndRewrite(PlusArgsTestOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
Definition SimToSV.cpp:64
LogicalResult matchAndRewrite(PlusArgsValueOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
Definition SimToSV.cpp:89
LogicalResult matchAndRewrite(FromOp op, typename FromOp::Adaptor adaptor, ConversionPatternRewriter &rewriter) const final
Definition SimToSV.cpp:154
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
void add(mlir::ModuleOp module)
Definition Namespace.h:48
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Definition Namespace.h:87
create(data_type, value)
Definition hw.py:433
create(dest, src)
Definition sv.py:98
create(value)
Definition sv.py:106
Definition sv.py:68
create(data_type, name=None, sym_name=None)
Definition sv.py:61
void setSVAttributes(mlir::Operation *op, mlir::ArrayAttr attrs)
Set the SV attributes of an operation.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createLowerSimToSVPass()
Definition SimToSV.cpp:379
circt::Namespace nameSpace
Definition SimToSV.cpp:249
void lower(sim::DPIFuncOp func)
Definition SimToSV.cpp:256
void addFragments(hw::HWModuleOp module, ArrayRef< StringAttr > dpiCallees) const
Definition SimToSV.cpp:297
llvm::DenseMap< StringAttr, StringAttr > symbolToFragment
Definition SimToSV.cpp:248
LowerDPIFunc(mlir::ModuleOp module)
Definition SimToSV.cpp:250