14#include "mlir/IR/ImplicitLocOpBuilder.h"
15#include "mlir/Pass/Pass.h"
16#include "llvm/Support/Debug.h"
19#define DEBUG_TYPE "arc-strip-sv"
23#define GEN_PASS_DEF_STRIPSV
24#include "circt/Dialect/Arc/ArcPasses.h.inc"
32struct StripSVPass :
public arc::impl::StripSVBase<StripSVPass> {
33 explicit StripSVPass(
bool asyncResetsAsSync) {
34 this->asyncResetsAsSync = asyncResetsAsSync;
36 void runOnOperation()
override;
37 SmallVector<Operation *> opsToDelete;
38 SmallPtrSet<StringAttr, 4> clockGateModuleNames;
42void StripSVPass::runOnOperation() {
43 auto mlirModule = getOperation();
45 clockGateModuleNames.clear();
47 auto expectedClockGateInputs =
48 ArrayAttr::get(&getContext(), {StringAttr::get(&getContext(),
"in"),
49 StringAttr::get(&getContext(),
"test_en"),
50 StringAttr::get(&getContext(),
"en")});
51 auto expectedClockGateOutputs =
52 ArrayAttr::get(&getContext(), {StringAttr::get(&getContext(),
"out")});
53 auto i1Type = IntegerType::get(&getContext(), 1);
55 for (
auto extModOp : mlirModule.getOps<
hw::HWModuleExternOp>()) {
56 if (extModOp.getVerilogModuleName() ==
"EICG_wrapper") {
57 if (!llvm::equal(extModOp.getInputNames(), expectedClockGateInputs) ||
58 !llvm::equal(extModOp.getOutputNames(), expectedClockGateOutputs)) {
59 extModOp.emitError(
"clock gate module `")
60 << extModOp.getModuleName() <<
"` has incompatible port names "
61 << extModOp.getInputNames() <<
" -> " << extModOp.getOutputNames();
62 return signalPassFailure();
64 if (!llvm::equal(extModOp.getInputTypes(),
65 ArrayRef<Type>{i1Type, i1Type, i1Type}) ||
66 !llvm::equal(extModOp.getOutputTypes(), ArrayRef<Type>{i1Type})) {
67 extModOp.emitError(
"clock gate module `")
68 << extModOp.getModuleName() <<
"` has incompatible port types "
69 << extModOp.getInputTypes() <<
" -> " << extModOp.getOutputTypes();
70 return signalPassFailure();
72 clockGateModuleNames.insert(extModOp.getModuleNameAttr());
73 opsToDelete.push_back(extModOp);
77 LLVM_DEBUG(llvm::dbgs() <<
"Found " << clockGateModuleNames.size()
81 mlirModule.walk([](Operation *op) {
82 auto isSVAttr = [](NamedAttribute attr) {
83 return attr.getName().getValue().starts_with(
"sv.");
85 if (llvm::any_of(op->getAttrs(), isSVAttr)) {
86 SmallVector<NamedAttribute> newAttrs;
87 newAttrs.reserve(op->getAttrs().size());
88 for (
auto attr : op->getAttrs())
90 newAttrs.push_back(attr);
91 op->setAttrs(newAttrs);
96 for (
auto verb : mlirModule.getOps<
sv::VerbatimOp>())
97 opsToDelete.push_back(verb);
98 for (
auto verb : mlirModule.getOps<
sv::IfDefOp>())
99 opsToDelete.push_back(verb);
100 for (
auto verb : mlirModule.getOps<
sv::MacroDeclOp>())
101 opsToDelete.push_back(verb);
103 for (
auto module : mlirModule.getOps<
hw::HWModuleOp>()) {
106 if (isa<sv::IfDefOp, sv::CoverOp, sv::CoverConcurrentOp>(&op)) {
107 opsToDelete.push_back(&op);
110 if (isa<sv::VerbatimOp, sv::AlwaysOp>(&op)) {
111 opsToDelete.push_back(&op);
116 if (
auto assign = dyn_cast<sv::AssignOp>(&op)) {
117 auto wire = assign.getDest().getDefiningOp<
sv::WireOp>();
119 assign.emitOpError(
"expected wire lhs");
120 return signalPassFailure();
122 for (Operation *user : wire->getUsers()) {
125 auto readInout = dyn_cast<sv::ReadInOutOp>(user);
127 user->emitOpError(
"has user that is not `sv.read_inout`");
128 return signalPassFailure();
130 readInout.replaceAllUsesWith(assign.getSrc());
131 opsToDelete.push_back(readInout);
133 opsToDelete.push_back(assign);
134 opsToDelete.push_back(wire);
139 if (
auto reg = dyn_cast<seq::FirRegOp>(&op)) {
140 OpBuilder builder(reg);
142 if (
reg.getIsAsync() && !asyncResetsAsSync) {
143 reg.emitOpError(
"only synchronous resets are currently supported");
144 return signalPassFailure();
149 if (
reg.getPreset() && !
reg.getPreset()->isZero()) {
150 assert(hw::type_isa<IntegerType>(
reg.getType()) &&
151 "cannot lower non integer preset");
153 builder,
reg.getLoc(),
154 IntegerAttr::get(
reg.getType(), *
reg.getPreset()));
158 builder,
reg.getLoc(),
reg.getType(),
reg.getNext(),
reg.getClk(),
159 reg.getNameAttr(),
reg.getReset(),
reg.getResetValue(),
160 presetValue,
reg.getInnerSymAttr());
161 reg.replaceAllUsesWith(compReg);
162 opsToDelete.push_back(reg);
168 if (
auto instOp = dyn_cast<hw::InstanceOp>(&op)) {
169 auto modName = instOp.getModuleNameAttr().getAttr();
170 ImplicitLocOpBuilder builder(instOp.getLoc(), instOp);
171 if (clockGateModuleNames.contains(modName)) {
172 auto gated = seq::ClockGateOp::create(
173 builder, instOp.getOperand(0), instOp.getOperand(1),
174 instOp.getOperand(2), hw::InnerSymAttr{});
175 instOp.replaceAllUsesWith(gated);
176 opsToDelete.push_back(instOp);
182 for (
auto *op : opsToDelete)
186std::unique_ptr<Pass> arc::createStripSVPass(
bool asyncResetsAsSync) {
187 return std::make_unique<StripSVPass>(asyncResetsAsSync);
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)
mlir::TypedValue< seq::ImmutableType > createConstantInitialValue(OpBuilder builder, Location loc, mlir::IntegerAttr attr)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)