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"
36 using namespace circt;
41 struct SimConversionState {
43 bool usedSynthesisMacro =
false;
44 SetVector<StringAttr> dpiCallees;
49 explicit SimConversionPattern(MLIRContext *context, SimConversionState &state)
52 SimConversionState &state;
61 using SimConversionPattern<PlusArgsTestOp>::SimConversionPattern;
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());
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);
86 using SimConversionPattern<PlusArgsValueOp>::SimConversionPattern;
90 ConversionPatternRewriter &rewriter)
const final {
91 auto loc = op.getLoc();
93 auto i1ty = rewriter.getIntegerType(1);
94 auto type = op.getResult().getType();
96 auto regv = rewriter.create<
sv::RegOp>(loc, type,
97 rewriter.getStringAttr(
"_pargs_v_"));
98 auto regf = rewriter.create<
sv::RegOp>(loc, i1ty,
99 rewriter.getStringAttr(
"_pargs_f"));
101 state.usedSynthesisMacro =
true;
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, regv, cstZ);
111 rewriter.getContext(),
112 "This dummy assignment exists to avoid undriven lint "
113 "warnings (e.g., Verilator UNDRIVEN).",
118 rewriter.
create<sv::InitialOp>(loc, [&] {
120 auto tmpResultType = rewriter.getIntegerType(32);
122 rewriter.
create<sv::ConstantStrOp>(loc, op.getFormatString());
123 auto call = rewriter.create<sv::SystemFunctionOp>(
124 loc, tmpResultType,
"value$plusargs",
125 ArrayRef<Value>{str, regv});
126 auto test = rewriter.create<comb::ICmpOp>(
127 loc, comb::ICmpPredicate::ne, call, zero32,
true);
128 rewriter.create<sv::BPAssignOp>(loc, regf, test);
134 rewriter.replaceOp(op, {readf, readv});
139 template <
typename FromOp,
typename ToOp>
142 using SimConversionPattern<FromOp>::SimConversionPattern;
146 ConversionPatternRewriter &rewriter)
const final {
147 auto loc = op.getLoc();
149 Value clockCast = rewriter.create<seq::FromClockOp>(loc, adaptor.getClk());
151 this->state.usedSynthesisMacro =
true;
153 loc,
"SYNTHESIS", [&] {},
155 rewriter.create<sv::AlwaysOp>(
156 loc, sv::EventControl::AtPosEdge, clockCast, [&] {
157 rewriter.create<sv::IfOp>(loc, adaptor.getCond(),
158 [&] { rewriter.create<ToOp>(loc); });
162 rewriter.eraseOp(op);
170 using SimConversionPattern<DPICallOp>::SimConversionPattern;
174 ConversionPatternRewriter &rewriter)
const final {
175 auto loc = op.getLoc();
177 state.dpiCallees.insert(op.getCalleeAttr().getAttr());
179 bool isClockedCall = !!op.getClock();
180 bool hasEnable = !!op.getEnable();
182 SmallVector<sv::RegOp> temporaries;
183 SmallVector<Value> reads;
184 for (
auto [type, result] :
185 llvm::zip(op.getResultTypes(), op.getResults())) {
186 temporaries.push_back(rewriter.create<
sv::RegOp>(op.getLoc(), type));
191 auto emitCall = [&]() {
192 auto call = rewriter.create<sv::FuncCallProceduralOp>(
193 op.getLoc(), op.getResultTypes(), op.getCalleeAttr(),
194 adaptor.getInputs());
195 for (
auto [lhs, rhs] : llvm::zip(temporaries, call.getResults())) {
197 rewriter.create<sv::PAssignOp>(op.getLoc(), lhs, rhs);
199 rewriter.create<sv::BPAssignOp>(op.getLoc(), lhs, rhs);
204 rewriter.create<seq::FromClockOp>(loc, adaptor.getClock());
205 rewriter.create<sv::AlwaysOp>(
206 loc, ArrayRef<sv::EventControl>{sv::EventControl::AtPosEdge},
207 ArrayRef<Value>{clockCast}, [&]() {
210 rewriter.create<sv::IfOp>(op.getLoc(), adaptor.getEnable(),
217 rewriter.create<sv::AlwaysCombOp>(loc, [&]() {
220 auto assignXToResults = [&] {
221 for (
auto lhs : temporaries) {
222 auto xValue = rewriter.create<sv::ConstantXOp>(
223 op.getLoc(), lhs.getType().getElementType());
224 rewriter.create<sv::BPAssignOp>(op.getLoc(), lhs, xValue);
227 rewriter.create<sv::IfOp>(op.getLoc(), adaptor.getEnable(), emitCall,
232 rewriter.replaceOp(op, reads);
242 void lower(sim::DPIFuncOp func);
244 ArrayRef<StringAttr> dpiCallees)
const;
248 ImplicitLocOpBuilder builder(func.getLoc(), func);
249 ArrayAttr inputLocsAttr, outputLocsAttr;
250 if (func.getArgumentLocs()) {
251 SmallVector<Attribute> inputLocs, outputLocs;
252 for (
auto [port, loc] :
253 llvm::zip(func.getModuleType().getPorts(),
254 func.getArgumentLocsAttr().getAsRange<LocationAttr>())) {
258 inputLocsAttr = builder.getArrayAttr(inputLocs);
259 outputLocsAttr = builder.getArrayAttr(outputLocs);
263 builder.create<sv::FuncOp>(func.getSymNameAttr(), func.getModuleType(),
264 func.getPerArgumentAttrsAttr(), inputLocsAttr,
265 outputLocsAttr, func.getVerilogNameAttr());
267 svFuncDecl.setPrivate();
268 auto name = builder.getStringAttr(nameSpace.newName(
269 func.getSymNameAttr().getValue(),
"dpi_import_fragument"));
272 auto macroDecl = builder.create<sv::MacroDeclOp>(nameSpace.newName(
273 "__CIRCT_DPI_IMPORT", func.getSymNameAttr().getValue().upper()));
274 builder.create<emit::FragmentOp>(name, [&]() {
276 macroDecl.getSymNameAttr(), []() {},
278 builder.create<sv::FuncDPIImportOp>(func.getSymNameAttr(),
280 builder.create<sv::MacroDefOp>(macroDecl.getSymNameAttr(),
"");
284 symbolToFragment.insert({func.getSymNameAttr(), name});
289 ArrayRef<StringAttr> dpiCallees)
const {
290 llvm::SetVector<Attribute> fragments;
292 if (
auto exstingFragments =
294 for (
auto fragment : exstingFragments.getAsRange<FlatSymbolRefAttr>())
295 fragments.insert(fragment);
296 for (
auto callee : dpiCallees) {
297 auto attr = symbolToFragment.at(callee);
300 if (!fragments.empty())
307 struct SimToSVPass :
public circt::impl::LowerSimToSVBase<SimToSVPass> {
308 void runOnOperation()
override {
309 auto circuit = getOperation();
310 MLIRContext *context = &getContext();
315 llvm::make_early_inc_range(circuit.getOps<sim::DPIFuncOp>()))
316 lowerDPIFunc.lower(func);
318 std::atomic<bool> usedSynthesisMacro =
false;
320 SimConversionState state;
321 ConversionTarget target(*context);
322 target.addIllegalDialect<SimDialect>();
323 target.addLegalDialect<sv::SVDialect>();
324 target.addLegalDialect<hw::HWDialect>();
325 target.addLegalDialect<seq::SeqDialect>();
326 target.addLegalDialect<comb::CombDialect>();
328 RewritePatternSet
patterns(context);
336 auto result = applyPartialConversion(module, target, std::move(
patterns));
342 lowerDPIFunc.addFragments(module, state.dpiCallees.takeVector());
344 if (state.usedSynthesisMacro)
345 usedSynthesisMacro =
true;
349 if (failed(mlir::failableParallelForEach(
351 return signalPassFailure();
353 if (usedSynthesisMacro) {
354 Operation *op = circuit.lookupSymbol(
"SYNTHESIS");
356 if (!isa<sv::MacroDeclOp>(op)) {
357 op->emitOpError(
"should be a macro declaration");
358 return signalPassFailure();
361 auto builder = ImplicitLocOpBuilder::atBlockBegin(
363 builder.create<sv::MacroDeclOp>(
"SYNTHESIS");
371 return std::make_unique<SimToSVPass>();
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
LogicalResult matchAndRewrite(FromOp op, typename FromOp::Adaptor 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)
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringRef getFragmentsAttrName()
Return the name of the fragments array attribute.
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()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
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)