13#include "mlir/Dialect/Arith/IR/Arith.h"
14#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
15#include "mlir/Dialect/Func/IR/FuncOps.h"
16#include "mlir/Dialect/SCF/IR/SCF.h"
17#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "arc-lower-verif-simulations"
29#define GEN_PASS_DEF_LOWERVERIFSIMULATIONSPASS
30#include "circt/Dialect/Arc/ArcPasses.h.inc"
35struct LowerVerifSimulationsPass
36 :
public arc::impl::LowerVerifSimulationsPassBase<
37 LowerVerifSimulationsPass> {
38 void runOnOperation()
override;
39 void lowerSimulation(verif::SimulationOp op, SymbolTable &symbolTable);
43void LowerVerifSimulationsPass::runOnOperation() {
44 SymbolTableCollection symbolTables;
47 auto builder = OpBuilder::atBlockBegin(getOperation().getBody());
48 auto exitFuncType = builder.getFunctionType({builder.getI32Type()}, {});
49 auto &symbolTable = symbolTables.getSymbolTable(getOperation());
50 if (
auto *exitOp = symbolTable.lookup(
"exit")) {
51 auto func = dyn_cast<func::FuncOp>(exitOp);
53 exitOp->emitOpError() <<
"expected to be a `func.func`";
54 return signalPassFailure();
56 if (func.getFunctionType() != exitFuncType) {
57 func.emitOpError() <<
"expected to have function type " << exitFuncType
58 <<
", got " << func.getFunctionType() <<
" instead";
59 return signalPassFailure();
62 auto func = builder.create<func::FuncOp>(
63 getOperation().getLoc(), builder.getStringAttr(
"exit"), exitFuncType);
64 SymbolTable::setSymbolVisibility(func, SymbolTable::Visibility::Private);
67 getOperation().walk([&](verif::SimulationOp op) {
68 auto *symbolTableOp = SymbolTable::getNearestSymbolTable(op);
70 lowerSimulation(op, symbolTables.getSymbolTable(symbolTableOp));
74void LowerVerifSimulationsPass::lowerSimulation(verif::SimulationOp op,
75 SymbolTable &symbolTable) {
76 LLVM_DEBUG(llvm::dbgs() <<
"Lowering " << op.getSymName() <<
"\n");
77 auto *context = &getContext();
78 auto i1Type = IntegerType::get(context, 1);
81 auto &body = *op.getBody();
82 auto *yieldOp = body.getTerminator();
83 std::array<PortInfo, 4> implPorts;
85 auto clockName = StringAttr::get(context,
"clock");
86 implPorts[0].name = clockName;
87 implPorts[0].type = seq::ClockType::get(context);
88 implPorts[0].dir = PortInfo::Input;
89 implPorts[0].loc = body.getArgument(0).getLoc();
91 auto initName = StringAttr::get(context,
"init");
92 implPorts[1].name = initName;
93 implPorts[1].type = i1Type;
94 implPorts[1].dir = PortInfo::Input;
95 implPorts[1].loc = body.getArgument(1).getLoc();
97 auto doneName = StringAttr::get(context,
"done");
98 implPorts[2].name = doneName;
99 implPorts[2].type = i1Type;
100 implPorts[2].dir = PortInfo::Output;
101 implPorts[2].loc = yieldOp->getOperand(0).getLoc();
103 auto successName = StringAttr::get(context,
"success");
104 implPorts[3].name = successName;
105 implPorts[3].type = i1Type;
106 implPorts[3].dir = PortInfo::Output;
107 implPorts[3].loc = yieldOp->getOperand(1).getLoc();
110 OpBuilder builder(yieldOp);
111 builder.create<hw::OutputOp>(yieldOp->getLoc(), yieldOp->getOperands());
115 builder.setInsertionPoint(op);
116 auto implName = StringAttr::get(context, Twine(
"verif.simulation.impl.") +
118 auto loc = op.getLoc();
119 auto implOp = builder.create<
hw::HWModuleOp>(loc, implName, implPorts);
120 symbolTable.insert(implOp);
121 implOp.getBody().takeBody(op.getBodyRegion());
124 auto funcType = builder.getFunctionType({}, {});
126 builder.create<func::FuncOp>(loc, op.getSymNameAttr(), funcType);
127 auto *funcBody = builder.createBlock(&funcOp.getBody());
131 auto lowOp = builder.
create<seq::ToClockOp>(loc, falseOp);
132 auto highOp = builder.create<seq::ToClockOp>(loc, trueOp);
135 auto instType = SimModelInstanceType::get(
136 context, FlatSymbolRefAttr::get(implOp.getSymNameAttr()));
137 auto instOp = builder.create<SimInstantiateOp>(loc);
139 builder.createBlock(&instOp.getBody(), {}, {instType}, {loc});
140 auto instArg = instBody->getArgument(0);
145 auto execOp = builder.create<scf::ExecuteRegionOp>(loc, TypeRange{});
146 builder.setInsertionPointToEnd(&execOp.getRegion().emplaceBlock());
149 builder.create<SimSetInputOp>(loc, instArg, clockName, lowOp);
150 builder.create<SimSetInputOp>(loc, instArg, initName, trueOp);
151 builder.create<SimStepOp>(loc, instArg);
152 builder.create<SimSetInputOp>(loc, instArg, clockName, highOp);
153 builder.create<SimStepOp>(loc, instArg);
154 builder.create<SimSetInputOp>(loc, instArg, clockName, lowOp);
155 builder.create<SimSetInputOp>(loc, instArg, initName, falseOp);
156 builder.create<SimStepOp>(loc, instArg);
159 auto &loopBlock = execOp.getRegion().emplaceBlock();
160 builder.create<cf::BranchOp>(loc, &loopBlock);
161 builder.setInsertionPointToEnd(&loopBlock);
165 builder.create<SimGetPortOp>(loc, i1Type, instArg, doneName);
167 builder.create<SimGetPortOp>(loc, i1Type, instArg, successName);
170 builder.create<SimSetInputOp>(loc, instArg, clockName, highOp);
171 builder.create<SimStepOp>(loc, instArg);
172 builder.create<SimSetInputOp>(loc, instArg, clockName, lowOp);
173 builder.create<SimStepOp>(loc, instArg);
176 auto &exitBlock = execOp.getRegion().emplaceBlock();
177 builder.create<cf::CondBranchOp>(loc, doneSample, &exitBlock, &loopBlock);
178 builder.setInsertionPointToEnd(&exitBlock);
182 auto i32Type = builder.getI32Type();
183 auto failureI32 = builder.create<arith::ExtUIOp>(
185 builder.create<arith::XOrIOp>(
186 loc, successSample, builder.create<
hw::ConstantOp>(loc, i1Type, 1)));
189 builder.create<func::CallOp>(loc, TypeRange{}, builder.getStringAttr(
"exit"),
190 ValueRange{failureI32});
191 builder.create<scf::YieldOp>(loc);
194 builder.setInsertionPointToEnd(funcBody);
195 builder.create<func::ReturnOp>(loc);
assert(baseType &&"element must be base type")
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.