CIRCT  20.0.0git
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 "circt/Dialect/SV/SVOps.h"
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 
23 namespace circt {
24 namespace arc {
25 #define GEN_PASS_DEF_STRIPSV
26 #include "circt/Dialect/Arc/ArcPasses.h.inc"
27 } // namespace arc
28 } // namespace circt
29 
30 using namespace circt;
31 using namespace arc;
32 
33 namespace {
34 struct StripSVPass : public arc::impl::StripSVBase<StripSVPass> {
35  void runOnOperation() override;
36  SmallVector<Operation *> opsToDelete;
37  SmallPtrSet<StringAttr, 4> clockGateModuleNames;
38 };
39 } // namespace
40 
41 void 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 
193 std::unique_ptr<Pass> arc::createStripSVPass() {
194  return std::make_unique<StripSVPass>();
195 }
assert(baseType &&"element must be base type")
Definition: sv.py:15
Definition: sv.py:35
std::unique_ptr< mlir::Pass > createStripSVPass()
Definition: StripSV.cpp:193
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
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: DebugAnalysis.h:21
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21