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"
29#define DEBUG_TYPE "lower-sim-to-sv"
32#define GEN_PASS_DEF_LOWERSIMTOSV
33#include "circt/Conversion/Passes.h.inc"
42 return isa<ClockedTerminateOp, ClockedPauseOp, TerminateOp, PauseOp>(op);
48 return TypeSwitch<Operation *, std::pair<Value, Value>>(op)
49 .Case<ClockedTerminateOp, ClockedPauseOp>(
50 [](
auto op) -> std::pair<Value, Value> {
51 return {op.getClock(), op.getCondition()};
58struct SimConversionState {
60 bool usedSynthesisMacro =
false;
61 SetVector<StringAttr> dpiCallees;
66 explicit SimConversionPattern(MLIRContext *context, SimConversionState &state)
69 SimConversionState &state;
78 using SimConversionPattern<PlusArgsTestOp>::SimConversionPattern;
82 ConversionPatternRewriter &rewriter)
const final {
83 auto loc = op.getLoc();
84 auto resultType = rewriter.getIntegerType(1);
85 auto str = sv::ConstantStrOp::create(rewriter, loc, op.getFormatString());
86 auto reg = sv::RegOp::create(rewriter, loc, resultType,
87 rewriter.getStringAttr(
"_pargs"));
88 sv::InitialOp::create(rewriter, loc, [&] {
89 auto call = sv::SystemFunctionOp::create(
90 rewriter, loc, resultType,
"test$plusargs", ArrayRef<Value>{str});
91 sv::BPAssignOp::create(rewriter, loc, reg, call);
103 using SimConversionPattern<PlusArgsValueOp>::SimConversionPattern;
107 ConversionPatternRewriter &rewriter)
const final {
108 auto loc = op.getLoc();
110 auto i1ty = rewriter.getIntegerType(1);
111 auto type = op.getResult().getType();
114 rewriter.getStringAttr(
"_pargs_v"));
116 rewriter.getStringAttr(
"_pargs_f"));
118 state.usedSynthesisMacro =
true;
120 rewriter, loc,
"SYNTHESIS",
123 auto cstZ = sv::ConstantZOp::create(rewriter, loc, type);
127 sv::SVAttributeAttr::get(
128 rewriter.getContext(),
129 "This dummy assignment exists to avoid undriven lint "
130 "warnings (e.g., Verilator UNDRIVEN).",
135 auto i32ty = rewriter.getIntegerType(32);
136 auto regf = sv::RegOp::create(rewriter, loc, i32ty,
137 rewriter.getStringAttr(
"_found"));
138 auto regv = sv::RegOp::create(rewriter, loc, type,
139 rewriter.getStringAttr(
"_value"));
140 sv::InitialOp::create(rewriter, loc, [&] {
142 sv::ConstantStrOp::create(rewriter, loc, op.getFormatString());
143 auto call = sv::SystemFunctionOp::create(
144 rewriter, loc, i32ty,
"value$plusargs",
145 ArrayRef<Value>{str, regv});
146 sv::BPAssignOp::create(rewriter, loc, regf, call);
152 auto cmp = comb::ICmpOp::create(
153 rewriter, loc, comb::ICmpPredicate::ceq, readRegF, cstTrue);
161 rewriter.replaceOp(op, {readf, readv});
166static LogicalResult
convert(ClockedTerminateOp op, PatternRewriter &rewriter) {
168 rewriter.replaceOpWithNewOp<sv::FinishOp>(op, op.getVerbose());
170 rewriter.replaceOpWithNewOp<sv::FatalOp>(op, op.getVerbose());
174static LogicalResult
convert(ClockedPauseOp op, PatternRewriter &rewriter) {
175 rewriter.replaceOpWithNewOp<sv::StopOp>(op, op.getVerbose());
179static LogicalResult
convert(TerminateOp op, PatternRewriter &rewriter) {
181 rewriter.replaceOpWithNewOp<sv::FinishOp>(op, op.getVerbose());
183 rewriter.replaceOpWithNewOp<sv::FatalOp>(op, op.getVerbose());
187static LogicalResult
convert(PauseOp op, PatternRewriter &rewriter) {
188 rewriter.replaceOpWithNewOp<sv::StopOp>(op, op.getVerbose());
194 using SimConversionPattern<DPICallOp>::SimConversionPattern;
198 ConversionPatternRewriter &rewriter)
const final {
199 auto loc = op.getLoc();
201 state.dpiCallees.insert(op.getCalleeAttr().getAttr());
203 bool isClockedCall = !!op.getClock();
204 bool hasEnable = !!op.getEnable();
206 SmallVector<sv::RegOp> temporaries;
207 SmallVector<Value> reads;
208 for (
auto [type, result] :
209 llvm::zip(op.getResultTypes(), op.getResults())) {
210 temporaries.push_back(sv::RegOp::create(rewriter, op.getLoc(), type));
215 auto emitCall = [&]() {
216 auto call = sv::FuncCallProceduralOp::create(
217 rewriter, op.getLoc(), op.getResultTypes(), op.getCalleeAttr(),
218 adaptor.getInputs());
219 for (
auto [lhs, rhs] : llvm::zip(temporaries, call.getResults())) {
221 sv::PAssignOp::create(rewriter, op.getLoc(), lhs, rhs);
223 sv::BPAssignOp::create(rewriter, op.getLoc(), lhs, rhs);
228 seq::FromClockOp::create(rewriter, loc, adaptor.getClock());
229 sv::AlwaysOp::create(
231 ArrayRef<sv::EventControl>{sv::EventControl::AtPosEdge},
232 ArrayRef<Value>{clockCast}, [&]() {
235 sv::IfOp::create(rewriter, op.getLoc(), adaptor.getEnable(),
242 sv::AlwaysCombOp::create(rewriter, loc, [&]() {
245 auto assignXToResults = [&] {
246 for (
auto lhs : temporaries) {
247 auto xValue = sv::ConstantXOp::create(
248 rewriter, op.getLoc(), lhs.getType().getElementType());
249 sv::BPAssignOp::create(rewriter, op.getLoc(), lhs, xValue);
252 sv::IfOp::create(rewriter, op.getLoc(), adaptor.getEnable(), emitCall,
257 rewriter.replaceOp(op, reads);
267 void lower(sim::DPIFuncOp func);
269 ArrayRef<StringAttr> dpiCallees)
const;
273 ImplicitLocOpBuilder builder(func.getLoc(), func);
274 ArrayAttr inputLocsAttr, outputLocsAttr;
275 if (func.getArgumentLocs()) {
276 SmallVector<Attribute> inputLocs, outputLocs;
277 for (
auto [port, loc] :
278 llvm::zip(func.getModuleType().getPorts(),
279 func.getArgumentLocsAttr().getAsRange<LocationAttr>())) {
283 inputLocsAttr = builder.getArrayAttr(inputLocs);
284 outputLocsAttr = builder.getArrayAttr(outputLocs);
288 sv::FuncOp::create(builder, func.getSymNameAttr(), func.getModuleType(),
289 func.getPerArgumentAttrsAttr(), inputLocsAttr,
290 outputLocsAttr, func.getVerilogNameAttr());
292 svFuncDecl.setPrivate();
294 func.getSymNameAttr().getValue(),
"dpi_import_fragument"));
297 auto macroDecl = sv::MacroDeclOp::create(
299 func.getSymNameAttr().getValue().upper()));
300 emit::FragmentOp::create(builder, name, [&]() {
302 builder, macroDecl.getSymNameAttr(), []() {},
304 sv::FuncDPIImportOp::create(builder, func.getSymNameAttr(),
306 sv::MacroDefOp::create(builder, macroDecl.getSymNameAttr(),
"");
315 ArrayRef<StringAttr> dpiCallees)
const {
316 llvm::SetVector<Attribute> fragments;
318 if (
auto exstingFragments =
319 module->getAttrOfType<ArrayAttr>(emit::getFragmentsAttrName()))
320 for (
auto fragment : exstingFragments.getAsRange<FlatSymbolRefAttr>())
321 fragments.insert(fragment);
322 for (
auto callee : dpiCallees) {
324 fragments.insert(FlatSymbolRefAttr::get(attr));
326 if (!fragments.empty())
328 emit::getFragmentsAttrName(),
329 ArrayAttr::get(module.getContext(), fragments.takeVector()));
333 bool usedSynthesisMacro =
false;
335 rootOp->walk([&](Operation *op) {
336 auto loc = op->getLoc();
341 Block *block =
nullptr;
342 if (op->getPrevNode())
343 block = TypeSwitch<Operation *, Block *>(op->getPrevNode())
345 [&](
auto guardOp) -> Block * {
346 if (guardOp.getCond().getIdent().getAttr() ==
349 return guardOp.getElseBlock();
352 .Default([](
auto) { return nullptr; });
356 OpBuilder builder(op);
358 block = sv::IfDefProceduralOp::create(
359 builder, loc,
"SYNTHESIS", [] {}, [] {})
362 block = sv::IfDefOp::create(
363 builder, loc,
"SYNTHESIS", [] {}, [] {})
365 usedSynthesisMacro =
true;
369 op->moveBefore(block, block->end());
378 Block *block =
nullptr;
379 if (
auto alwaysOp = dyn_cast_or_null<sv::AlwaysOp>(op->getPrevNode()))
380 if (alwaysOp.getNumConditions() == 1 &&
381 alwaysOp.getCondition(0).event == sv::EventControl::AtPosEdge)
382 if (
auto clockOp = alwaysOp.getCondition(0)
383 .value.getDefiningOp<seq::FromClockOp>())
384 if (clockOp.getInput() == clock)
385 block = alwaysOp.getBodyBlock();
389 OpBuilder builder(op);
390 clock = seq::FromClockOp::create(builder, loc, clock);
391 block = sv::AlwaysOp::create(builder, loc, sv::EventControl::AtPosEdge,
397 op->moveBefore(block, block->end());
403 Block *block =
nullptr;
404 if (
auto ifOp = dyn_cast_or_null<sv::IfOp>(op->getPrevNode()))
405 if (ifOp.getCond() == condition)
406 block = ifOp.getThenBlock();
410 OpBuilder builder(op);
411 block = sv::IfOp::create(builder, loc, condition, [] {}).getThenBlock();
415 op->moveBefore(block, block->end());
419 return usedSynthesisMacro;
423struct SimToSVPass :
public circt::impl::LowerSimToSVBase<SimToSVPass> {
424 void runOnOperation()
override {
425 auto circuit = getOperation();
426 MLIRContext *context = &getContext();
431 llvm::make_early_inc_range(circuit.getOps<sim::DPIFuncOp>()))
432 lowerDPIFunc.lower(func);
434 std::atomic<bool> usedSynthesisMacro =
false;
437 usedSynthesisMacro =
true;
439 SimConversionState state;
440 ConversionTarget target(*context);
441 target.addIllegalDialect<SimDialect>();
442 target.addLegalDialect<sv::SVDialect>();
443 target.addLegalDialect<hw::HWDialect>();
444 target.addLegalDialect<seq::SeqDialect>();
445 target.addLegalDialect<comb::CombDialect>();
447 RewritePatternSet
patterns(context);
455 auto result = applyPartialConversion(module, target, std::move(
patterns));
461 lowerDPIFunc.addFragments(module, state.dpiCallees.takeVector());
463 if (state.usedSynthesisMacro)
464 usedSynthesisMacro =
true;
468 if (failed(mlir::failableParallelForEach(
470 return signalPassFailure();
472 if (usedSynthesisMacro) {
473 Operation *op = circuit.lookupSymbol(
"SYNTHESIS");
475 if (!isa<sv::MacroDeclOp>(op)) {
476 op->emitOpError(
"should be a macro declaration");
477 return signalPassFailure();
480 auto builder = ImplicitLocOpBuilder::atBlockBegin(
481 UnknownLoc::get(context), circuit.getBody());
482 sv::MacroDeclOp::create(builder,
"SYNTHESIS");
490 return std::make_unique<SimToSVPass>();
static Block * getBodyBlock(FModuleLike mod)
static std::pair< Value, Value > needsClockAndConditionWrapper(Operation *op)
Check whether an op should be placed inside an always process triggered on a clock,...
static bool moveOpsIntoIfdefGuardsAndProcesses(Operation *rootOp)
static LogicalResult convert(ClockedTerminateOp op, PatternRewriter &rewriter)
static bool needsIfdefGuard(Operation *op)
Check whether an op should be placed inside an ifdef guard that prevents it from affecting synthesis ...
LogicalResult matchAndRewrite(DPICallOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
LogicalResult matchAndRewrite(PlusArgsTestOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
LogicalResult matchAndRewrite(PlusArgsValueOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const final
A namespace that is used to store existing names and generate new names in some scope within the IR.
void add(mlir::ModuleOp module)
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
Signals that an operations regions are procedural.
create(data_type, name=None, sym_name=None)
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()
circt::Namespace nameSpace
void lower(sim::DPIFuncOp func)
void addFragments(hw::HWModuleOp module, ArrayRef< StringAttr > dpiCallees) const
llvm::DenseMap< StringAttr, StringAttr > symbolToFragment
LowerDPIFunc(mlir::ModuleOp module)