CIRCT  20.0.0git
VerifToSV.cpp
Go to the documentation of this file.
1 //===- VerifToSV.cpp - HW To SV Conversion Pass ---------------------------===//
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 //
9 // This is the main Verif to SV Conversion Pass Implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
16 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Dialect/SV/SVOps.h"
20 #include "mlir/Pass/Pass.h"
21 #include "mlir/Transforms/DialectConversion.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 
24 namespace circt {
25 #define GEN_PASS_DEF_LOWERVERIFTOSV
26 #include "circt/Conversion/Passes.h.inc"
27 } // namespace circt
28 
29 using namespace mlir;
30 using namespace circt;
31 using namespace sv;
32 using namespace verif;
33 
34 //===----------------------------------------------------------------------===//
35 // Conversion Patterns
36 //===----------------------------------------------------------------------===//
37 
38 namespace {
39 struct PrintOpConversionPattern : public OpConversionPattern<PrintOp> {
41 
42  LogicalResult
43  matchAndRewrite(PrintOp op, OpAdaptor operands,
44  ConversionPatternRewriter &rewriter) const override {
45 
46  // Printf's will be emitted to stdout (32'h8000_0001 in IEEE Std 1800-2012).
47  Value fdStdout = rewriter.create<hw::ConstantOp>(
48  op.getLoc(), APInt(32, 0x80000001, false));
49 
50  auto fstrOp =
51  dyn_cast_or_null<FormatVerilogStringOp>(op.getString().getDefiningOp());
52  if (!fstrOp)
53  return op->emitOpError() << "expected FormatVerilogStringOp as the "
54  "source of the formatted string";
55 
56  rewriter.replaceOpWithNewOp<sv::FWriteOp>(
57  op, fdStdout, fstrOp.getFormatString(), fstrOp.getSubstitutions());
58  return success();
59  }
60 };
61 
62 struct HasBeenResetConversion : public OpConversionPattern<HasBeenResetOp> {
64 
65  LogicalResult
66  matchAndRewrite(HasBeenResetOp op, OpAdaptor operands,
67  ConversionPatternRewriter &rewriter) const override {
68  auto i1 = rewriter.getI1Type();
69  auto constOne = rewriter.create<hw::ConstantOp>(op.getLoc(), i1, 1);
70  auto constZero = rewriter.create<hw::ConstantOp>(op.getLoc(), i1, 0);
71  auto constX = rewriter.create<sv::ConstantXOp>(op.getLoc(), i1);
72 
73  // Declare the register that will track the reset state.
74  auto reg = rewriter.create<sv::RegOp>(
75  op.getLoc(), i1, rewriter.getStringAttr("hasBeenResetReg"));
76 
77  auto clock = operands.getClock();
78  auto reset = operands.getReset();
79 
80  // Explicitly initialize the register in an `initial` block. In general, the
81  // register will come up as X, but this may be overridden by simulator
82  // configuration options.
83  //
84  // In case the reset is async, check if the reset is already active during
85  // the `initial` block and immediately set the register to 1. Otherwise
86  // initialize to X.
87  rewriter.create<sv::InitialOp>(op.getLoc(), [&] {
88  auto assignOne = [&] {
89  rewriter.create<sv::BPAssignOp>(op.getLoc(), reg, constOne);
90  };
91  auto assignX = [&] {
92  rewriter.create<sv::BPAssignOp>(op.getLoc(), reg, constX);
93  };
94  if (op.getAsync())
95  rewriter.create<sv::IfOp>(op.getLoc(), reset, assignOne, assignX);
96  else
97  assignX();
98  });
99 
100  // Create the `always` block that sets the register to 1 as soon as the
101  // reset is initiated. For async resets this happens at the reset's posedge;
102  // for sync resets this happens on the clock's posedge if the reset is set.
103  Value triggerOn = op.getAsync() ? reset : clock;
104  rewriter.create<sv::AlwaysOp>(
105  op.getLoc(), sv::EventControl::AtPosEdge, triggerOn, [&] {
106  auto assignOne = [&] {
107  rewriter.create<sv::PAssignOp>(op.getLoc(), reg, constOne);
108  };
109  if (op.getAsync())
110  assignOne();
111  else
112  rewriter.create<sv::IfOp>(op.getLoc(), reset, assignOne);
113  });
114 
115  // Derive the actual result value:
116  // hasBeenReset = (hasBeenResetReg === 1) && (reset === 0);
117  auto regRead = rewriter.create<sv::ReadInOutOp>(op.getLoc(), reg);
118  auto regIsOne = rewriter.createOrFold<comb::ICmpOp>(
119  op.getLoc(), comb::ICmpPredicate::ceq, regRead, constOne);
120  auto resetIsZero = rewriter.createOrFold<comb::ICmpOp>(
121  op.getLoc(), comb::ICmpPredicate::ceq, reset, constZero);
122  auto resetStartedAndEnded = rewriter.createOrFold<comb::AndOp>(
123  op.getLoc(), regIsOne, resetIsZero, true);
124  rewriter.replaceOpWithNewOp<hw::WireOp>(
125  op, resetStartedAndEnded, rewriter.getStringAttr("hasBeenReset"));
126 
127  return success();
128  }
129 };
130 
131 // Conversion from one event control enum to another
132 static sv::EventControl verifToSVEventControl(verif::ClockEdge ce) {
133  switch (ce) {
134  case verif::ClockEdge::Pos:
135  return sv::EventControl::AtPosEdge;
136  case verif::ClockEdge::Neg:
137  return sv::EventControl::AtNegEdge;
138  case verif::ClockEdge::Both:
139  return sv::EventControl::AtEdge;
140  }
141  llvm_unreachable("Unknown event control kind");
142 }
143 
144 // Generic conversion for verif.assert, verif.assume and verif.cover
145 template <typename Op, typename TargetOp>
146 struct VerifAssertLikeConversion : public OpConversionPattern<Op> {
148  using OpAdaptor = typename OpConversionPattern<Op>::OpAdaptor;
149 
150  // Convert the verif.assert like op that uses an enable, into an equivalent
151  // sv.assert_property op that has the negated enable as its disable.
152  LogicalResult
153  matchAndRewrite(Op op, OpAdaptor operands,
154  ConversionPatternRewriter &rewriter) const override {
155  // Negate the enable if it exists
156  Value disable;
157  if (auto enable = operands.getEnable()) {
158  Value constOne = rewriter.createOrFold<hw::ConstantOp>(
159  op.getLoc(), rewriter.getI1Type(), 1);
160  disable =
161  rewriter.createOrFold<comb::XorOp>(op.getLoc(), enable, constOne);
162  }
163 
164  rewriter.replaceOpWithNewOp<TargetOp>(op, operands.getProperty(), disable,
165  operands.getLabelAttr());
166 
167  return success();
168  }
169 };
170 
171 // Generic conversion for verif.clocked_assert, verif.clocked_assume and
172 // verif.clocked_cover
173 template <typename Op, typename TargetOp>
174 struct VerifClockedAssertLikeConversion : public OpConversionPattern<Op> {
176  using OpAdaptor = typename OpConversionPattern<Op>::OpAdaptor;
177 
178  // Convert the verif.assert like op that uses an enable, into an equivalent
179  // sv.assert_property op that has the negated enable as its disable.
180  LogicalResult
181  matchAndRewrite(Op op, OpAdaptor operands,
182  ConversionPatternRewriter &rewriter) const override {
183  // Negate the enable if it exists
184  Value disable;
185  if (auto enable = operands.getEnable()) {
186  Value constOne = rewriter.createOrFold<hw::ConstantOp>(
187  op.getLoc(), rewriter.getI1Type(), 1);
188  disable =
189  rewriter.createOrFold<comb::XorOp>(op.getLoc(), enable, constOne);
190  }
191  // Convert a verif clock edge into an sv event control
192  auto eventattr = sv::EventControlAttr::get(
193  op.getContext(), verifToSVEventControl(operands.getEdge()));
194 
195  rewriter.replaceOpWithNewOp<TargetOp>(op, operands.getProperty(), eventattr,
196  operands.getClock(), disable,
197  operands.getLabelAttr());
198 
199  return success();
200  }
201 };
202 
203 } // namespace
204 
205 //===----------------------------------------------------------------------===//
206 // Pass Infrastructure
207 //===----------------------------------------------------------------------===//
208 
209 namespace {
210 struct VerifToSVPass : public circt::impl::LowerVerifToSVBase<VerifToSVPass> {
211  void runOnOperation() override;
212 };
213 } // namespace
214 
215 void VerifToSVPass::runOnOperation() {
216  MLIRContext &context = getContext();
217  hw::HWModuleOp module = getOperation();
218 
219  ConversionTarget target(context);
220  RewritePatternSet patterns(&context);
221 
222  target.addIllegalOp<PrintOp, HasBeenResetOp, verif::AssertOp, verif::AssumeOp,
223  verif::CoverOp, ClockedAssertOp, ClockedAssumeOp,
224  ClockedCoverOp>();
225  target.addLegalDialect<sv::SVDialect, hw::HWDialect, comb::CombDialect>();
226  patterns.add<
227  PrintOpConversionPattern, HasBeenResetConversion,
228  VerifAssertLikeConversion<verif::AssertOp, AssertPropertyOp>,
229  VerifAssertLikeConversion<verif::AssumeOp, AssumePropertyOp>,
230  VerifAssertLikeConversion<verif::CoverOp, CoverPropertyOp>,
231  VerifClockedAssertLikeConversion<verif::ClockedAssertOp,
232  AssertPropertyOp>,
233  VerifClockedAssertLikeConversion<verif::ClockedAssumeOp,
234  AssumePropertyOp>,
235  VerifClockedAssertLikeConversion<verif::ClockedCoverOp, CoverPropertyOp>>(
236  &context);
237 
238  if (failed(applyPartialConversion(module, target, std::move(patterns))))
239  signalPassFailure();
240 }
241 
242 std::unique_ptr<OperationPass<hw::HWModuleOp>>
244  return std::make_unique<VerifToSVPass>();
245 }
def create(data_type, value)
Definition: hw.py:393
Definition: sv.py:68
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< OperationPass< hw::HWModuleOp > > createLowerVerifToSVPass()
Create the Verif to SV conversion pass.
Definition: VerifToSV.cpp:243
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:21
Definition: sv.py:1
Definition: verif.py:1