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