CIRCT 22.0.0git
Loading...
Searching...
No Matches
LowerVerifSimulations.cpp
Go to the documentation of this file.
1//===- LowerVerifSimulations.cpp ------------------------------------------===//
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
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"
18
19#define DEBUG_TYPE "arc-lower-verif-simulations"
20
21using namespace mlir;
22using namespace circt;
23using namespace arc;
24
25using hw::PortInfo;
26
27namespace circt {
28namespace arc {
29#define GEN_PASS_DEF_LOWERVERIFSIMULATIONSPASS
30#include "circt/Dialect/Arc/ArcPasses.h.inc"
31} // namespace arc
32} // namespace circt
33
34namespace {
35struct LowerVerifSimulationsPass
36 : public arc::impl::LowerVerifSimulationsPassBase<
37 LowerVerifSimulationsPass> {
38 void runOnOperation() override;
39 void lowerSimulation(verif::SimulationOp op, SymbolTable &symbolTable);
40};
41} // namespace
42
43void LowerVerifSimulationsPass::runOnOperation() {
44 SymbolTableCollection symbolTables;
45
46 // Declare the `exit` function if it does not yet exist.
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);
52 if (!func) {
53 exitOp->emitOpError() << "expected to be a `func.func`";
54 return signalPassFailure();
55 }
56 if (func.getFunctionType() != exitFuncType) {
57 func.emitOpError() << "expected to have function type " << exitFuncType
58 << ", got " << func.getFunctionType() << " instead";
59 return signalPassFailure();
60 }
61 } else {
62 auto func =
63 func::FuncOp::create(builder, getOperation().getLoc(),
64 builder.getStringAttr("exit"), exitFuncType);
65 SymbolTable::setSymbolVisibility(func, SymbolTable::Visibility::Private);
66 }
67
68 getOperation().walk([&](verif::SimulationOp op) {
69 auto *symbolTableOp = SymbolTable::getNearestSymbolTable(op);
70 assert(symbolTableOp);
71 lowerSimulation(op, symbolTables.getSymbolTable(symbolTableOp));
72 });
73}
74
75void LowerVerifSimulationsPass::lowerSimulation(verif::SimulationOp op,
76 SymbolTable &symbolTable) {
77 LLVM_DEBUG(llvm::dbgs() << "Lowering " << op.getSymName() << "\n");
78 auto *context = &getContext();
79 auto i1Type = IntegerType::get(context, 1);
80
81 // Assemble the ports of the implementation module.
82 auto &body = *op.getBody();
83 auto *yieldOp = body.getTerminator();
84 std::array<PortInfo, 4> implPorts;
85
86 auto clockName = StringAttr::get(context, "clock");
87 implPorts[0].name = clockName;
88 implPorts[0].type = seq::ClockType::get(context);
89 implPorts[0].dir = PortInfo::Input;
90 implPorts[0].loc = body.getArgument(0).getLoc();
91
92 auto initName = StringAttr::get(context, "init");
93 implPorts[1].name = initName;
94 implPorts[1].type = i1Type;
95 implPorts[1].dir = PortInfo::Input;
96 implPorts[1].loc = body.getArgument(1).getLoc();
97
98 auto doneName = StringAttr::get(context, "done");
99 implPorts[2].name = doneName;
100 implPorts[2].type = i1Type;
101 implPorts[2].dir = PortInfo::Output;
102 implPorts[2].loc = yieldOp->getOperand(0).getLoc();
103
104 auto successName = StringAttr::get(context, "success");
105 implPorts[3].name = successName;
106 implPorts[3].type = i1Type;
107 implPorts[3].dir = PortInfo::Output;
108 implPorts[3].loc = yieldOp->getOperand(1).getLoc();
109
110 // Replace the `verif.yield` operation with an `hw.output`.
111 OpBuilder builder(yieldOp);
112 hw::OutputOp::create(builder, yieldOp->getLoc(), yieldOp->getOperands());
113 yieldOp->erase();
114
115 // Move the body of the simulation into a separate HW module.
116 builder.setInsertionPoint(op);
117 auto implName = StringAttr::get(context, Twine("verif.simulation.impl.") +
118 op.getSymName());
119 auto loc = op.getLoc();
120 auto implOp = hw::HWModuleOp::create(builder, loc, implName, implPorts);
121 symbolTable.insert(implOp);
122 implOp.getBody().takeBody(op.getBodyRegion());
123
124 // Create a new function for the verification op.
125 auto funcType = builder.getFunctionType({}, {});
126 auto funcOp =
127 func::FuncOp::create(builder, loc, op.getSymNameAttr(), funcType);
128 auto *funcBody = builder.createBlock(&funcOp.getBody());
129
130 auto falseOp = hw::ConstantOp::create(builder, loc, i1Type, 0);
131 auto trueOp = hw::ConstantOp::create(builder, loc, i1Type, 1);
132 auto lowOp = seq::ToClockOp::create(builder, loc, falseOp);
133 auto highOp = seq::ToClockOp::create(builder, loc, trueOp);
134
135 // Instantiate the implementation module.
136 auto instType = SimModelInstanceType::get(
137 context, FlatSymbolRefAttr::get(implOp.getSymNameAttr()));
138 auto instOp = SimInstantiateOp::create(builder, loc);
139 auto *instBody =
140 builder.createBlock(&instOp.getBody(), {}, {instType}, {loc});
141 auto instArg = instBody->getArgument(0);
142
143 // Create an `scf.execute_region` op inside such that we can use simple
144 // control flow in the `arc.sim.instantiate` op body. This is simpler than
145 // setting up an `scf.while` op.
146 auto execOp = scf::ExecuteRegionOp::create(builder, loc, TypeRange{});
147 builder.setInsertionPointToEnd(&execOp.getRegion().emplaceBlock());
148
149 // Apply the initial clock tick to the design.
150 SimSetInputOp::create(builder, loc, instArg, clockName, lowOp);
151 SimSetInputOp::create(builder, loc, instArg, initName, trueOp);
152 SimStepOp::create(builder, loc, instArg);
153 SimSetInputOp::create(builder, loc, instArg, clockName, highOp);
154 SimStepOp::create(builder, loc, instArg);
155 SimSetInputOp::create(builder, loc, instArg, clockName, lowOp);
156 SimSetInputOp::create(builder, loc, instArg, initName, falseOp);
157 SimStepOp::create(builder, loc, instArg);
158
159 // Create the block that will perform a single clock tick.
160 auto &loopBlock = execOp.getRegion().emplaceBlock();
161 cf::BranchOp::create(builder, loc, &loopBlock);
162 builder.setInsertionPointToEnd(&loopBlock);
163
164 // Sample the done and success signals.
165 auto doneSample =
166 SimGetPortOp::create(builder, loc, i1Type, instArg, doneName);
167 auto successSample =
168 SimGetPortOp::create(builder, loc, i1Type, instArg, successName);
169
170 // Apply a full clock cycle to the design.
171 SimSetInputOp::create(builder, loc, instArg, clockName, highOp);
172 SimStepOp::create(builder, loc, instArg);
173 SimSetInputOp::create(builder, loc, instArg, clockName, lowOp);
174 SimStepOp::create(builder, loc, instArg);
175
176 // If done, exit the loop.
177 auto &exitBlock = execOp.getRegion().emplaceBlock();
178 cf::CondBranchOp::create(builder, loc, doneSample, &exitBlock, &loopBlock);
179 builder.setInsertionPointToEnd(&exitBlock);
180
181 // Convert the i1 success signal into an i32 failure signal that can be used
182 // as an exit code.
183 auto i32Type = builder.getI32Type();
184 auto failureI32 = arith::ExtUIOp::create(
185 builder, loc, i32Type,
186 arith::XOrIOp::create(builder, loc, successSample,
187 hw::ConstantOp::create(builder, loc, i1Type, 1)));
188
189 // Call exit with the computed exit code.
190 func::CallOp::create(builder, loc, TypeRange{}, builder.getStringAttr("exit"),
191 ValueRange{failureI32});
192 scf::YieldOp::create(builder, loc);
193
194 // Create the final function return.
195 builder.setInsertionPointToEnd(funcBody);
196 func::ReturnOp::create(builder, loc);
197
198 // Get rid of the original simulation op.
199 op.erase();
200}
assert(baseType &&"element must be base type")
static Location getLoc(DefSlot slot)
Definition Mem2Reg.cpp:216
create(data_type, value)
Definition hw.py:433
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.