CIRCT 20.0.0git
Loading...
Searching...
No Matches
StripSV.cpp
Go to the documentation of this file.
1//===- StripSV.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
16#include "mlir/IR/ImplicitLocOpBuilder.h"
17#include "mlir/Pass/Pass.h"
18#include "llvm/Support/Debug.h"
19#include <variant>
20
21#define DEBUG_TYPE "arc-strip-sv"
22
23namespace circt {
24namespace arc {
25#define GEN_PASS_DEF_STRIPSV
26#include "circt/Dialect/Arc/ArcPasses.h.inc"
27} // namespace arc
28} // namespace circt
29
30using namespace circt;
31using namespace arc;
32
33namespace {
34struct StripSVPass : public arc::impl::StripSVBase<StripSVPass> {
35 void runOnOperation() override;
36 SmallVector<Operation *> opsToDelete;
37 SmallPtrSet<StringAttr, 4> clockGateModuleNames;
38};
39} // namespace
40
41void StripSVPass::runOnOperation() {
42 auto mlirModule = getOperation();
43 opsToDelete.clear();
44 clockGateModuleNames.clear();
45
46 auto expectedClockGateInputs =
47 ArrayAttr::get(&getContext(), {StringAttr::get(&getContext(), "in"),
48 StringAttr::get(&getContext(), "test_en"),
49 StringAttr::get(&getContext(), "en")});
50 auto expectedClockGateOutputs =
51 ArrayAttr::get(&getContext(), {StringAttr::get(&getContext(), "out")});
52 auto i1Type = IntegerType::get(&getContext(), 1);
53
54 for (auto extModOp : mlirModule.getOps<hw::HWModuleExternOp>()) {
55 if (extModOp.getVerilogModuleName() == "EICG_wrapper") {
56 if (!llvm::equal(extModOp.getInputNames(), expectedClockGateInputs) ||
57 !llvm::equal(extModOp.getOutputNames(), expectedClockGateOutputs)) {
58 extModOp.emitError("clock gate module `")
59 << extModOp.getModuleName() << "` has incompatible port names "
60 << extModOp.getInputNames() << " -> " << extModOp.getOutputNames();
61 return signalPassFailure();
62 }
63 if (!llvm::equal(extModOp.getInputTypes(),
64 ArrayRef<Type>{i1Type, i1Type, i1Type}) ||
65 !llvm::equal(extModOp.getOutputTypes(), ArrayRef<Type>{i1Type})) {
66 extModOp.emitError("clock gate module `")
67 << extModOp.getModuleName() << "` has incompatible port types "
68 << extModOp.getInputTypes() << " -> " << extModOp.getOutputTypes();
69 return signalPassFailure();
70 }
71 clockGateModuleNames.insert(extModOp.getModuleNameAttr());
72 opsToDelete.push_back(extModOp);
73 continue;
74 }
75 }
76 LLVM_DEBUG(llvm::dbgs() << "Found " << clockGateModuleNames.size()
77 << " clock gates\n");
78
79 // Remove OM and Emit dialect nodes.
80 for (auto &op : llvm::make_early_inc_range(*mlirModule.getBody()))
81 if (isa<emit::EmitDialect, om::OMDialect>(op.getDialect()))
82 op.erase();
83
84 // Remove `sv.*` operation attributes.
85 mlirModule.walk([](Operation *op) {
86 auto isSVAttr = [](NamedAttribute attr) {
87 return attr.getName().getValue().starts_with("sv.");
88 };
89 if (llvm::any_of(op->getAttrs(), isSVAttr)) {
90 SmallVector<NamedAttribute> newAttrs;
91 newAttrs.reserve(op->getAttrs().size());
92 for (auto attr : op->getAttrs())
93 if (!isSVAttr(attr))
94 newAttrs.push_back(attr);
95 op->setAttrs(newAttrs);
96 }
97 });
98
99 // Remove ifdefs and verbatim.
100 for (auto verb : mlirModule.getOps<sv::VerbatimOp>())
101 opsToDelete.push_back(verb);
102 for (auto verb : mlirModule.getOps<sv::IfDefOp>())
103 opsToDelete.push_back(verb);
104 for (auto verb : mlirModule.getOps<sv::MacroDeclOp>())
105 opsToDelete.push_back(verb);
106
107 for (auto module : mlirModule.getOps<hw::HWModuleOp>()) {
108 for (Operation &op : *module.getBodyBlock()) {
109 // Remove ifdefs and verbatim.
110 if (isa<sv::IfDefOp, sv::CoverOp, sv::CoverConcurrentOp>(&op)) {
111 opsToDelete.push_back(&op);
112 continue;
113 }
114 if (isa<sv::VerbatimOp, sv::AlwaysOp>(&op)) {
115 opsToDelete.push_back(&op);
116 continue;
117 }
118
119 // Remove wires.
120 if (auto assign = dyn_cast<sv::AssignOp>(&op)) {
121 auto wire = assign.getDest().getDefiningOp<sv::WireOp>();
122 if (!wire) {
123 assign.emitOpError("expected wire lhs");
124 return signalPassFailure();
125 }
126 for (Operation *user : wire->getUsers()) {
127 if (user == assign)
128 continue;
129 auto readInout = dyn_cast<sv::ReadInOutOp>(user);
130 if (!readInout) {
131 user->emitOpError("has user that is not `sv.read_inout`");
132 return signalPassFailure();
133 }
134 readInout.replaceAllUsesWith(assign.getSrc());
135 opsToDelete.push_back(readInout);
136 }
137 opsToDelete.push_back(assign);
138 opsToDelete.push_back(wire);
139 continue;
140 }
141
142 // Canonicalize registers.
143 if (auto reg = dyn_cast<seq::FirRegOp>(&op)) {
144 OpBuilder builder(reg);
145 Value next;
146 // Note: this register will have an sync reset regardless.
147 if (reg.hasReset())
148 next = builder.create<comb::MuxOp>(reg.getLoc(), reg.getReset(),
149 reg.getResetValue(), reg.getNext(),
150 false);
151 else
152 next = reg.getNext();
153
154 Value presetValue;
155 // Materialize initial value, assume zero initialization as default.
156 if (reg.getPreset() && !reg.getPreset()->isZero()) {
157 assert(hw::type_isa<IntegerType>(reg.getType()) &&
158 "cannot lower non integer preset");
160 builder, reg.getLoc(),
161 IntegerAttr::get(reg.getType(), *reg.getPreset()));
162 }
163
164 Value compReg = builder.create<seq::CompRegOp>(
165 reg.getLoc(), next.getType(), next, reg.getClk(), reg.getNameAttr(),
166 Value{}, Value{}, /*initialValue*/ presetValue,
167 reg.getInnerSymAttr());
168 reg.replaceAllUsesWith(compReg);
169 opsToDelete.push_back(reg);
170 continue;
171 }
172
173 // Replace clock gate instances with the dedicated `seq.clock_gate` op and
174 // stub out other external modules.
175 if (auto instOp = dyn_cast<hw::InstanceOp>(&op)) {
176 auto modName = instOp.getModuleNameAttr().getAttr();
177 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
178 if (clockGateModuleNames.contains(modName)) {
179 auto gated = builder.create<seq::ClockGateOp>(
180 instOp.getOperand(0), instOp.getOperand(1), instOp.getOperand(2),
181 hw::InnerSymAttr{});
182 instOp.replaceAllUsesWith(gated);
183 opsToDelete.push_back(instOp);
184 }
185 continue;
186 }
187 }
188 }
189 for (auto *op : opsToDelete)
190 op->erase();
191}
192
193std::unique_ptr<Pass> arc::createStripSVPass() {
194 return std::make_unique<StripSVPass>();
195}
assert(baseType &&"element must be base type")
static Block * getBodyBlock(FModuleLike mod)
std::unique_ptr< mlir::Pass > createStripSVPass()
Definition StripSV.cpp:193
mlir::TypedValue< seq::ImmutableType > createConstantInitialValue(OpBuilder builder, Location loc, mlir::IntegerAttr attr)
Definition SeqOps.cpp:1065
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition emit.py:1
Definition hw.py:1
Definition om.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
Definition sv.py:1