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