mirror of https://github.com/llvm/circt.git
246 lines
9.4 KiB
C++
246 lines
9.4 KiB
C++
//===- VerifToSV.cpp - HW To SV Conversion Pass ---------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This is the main Verif to SV Conversion Pass Implementation.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "circt/Conversion/VerifToSV.h"
|
|
#include "circt/Dialect/Comb/CombOps.h"
|
|
#include "circt/Dialect/HW/HWOpInterfaces.h"
|
|
#include "circt/Dialect/HW/HWOps.h"
|
|
#include "circt/Dialect/SV/SVDialect.h"
|
|
#include "circt/Dialect/SV/SVOps.h"
|
|
#include "circt/Dialect/Verif/VerifOps.h"
|
|
#include "mlir/Pass/Pass.h"
|
|
#include "mlir/Transforms/DialectConversion.h"
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
|
|
namespace circt {
|
|
#define GEN_PASS_DEF_LOWERVERIFTOSV
|
|
#include "circt/Conversion/Passes.h.inc"
|
|
} // namespace circt
|
|
|
|
using namespace mlir;
|
|
using namespace circt;
|
|
using namespace sv;
|
|
using namespace verif;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Conversion Patterns
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct PrintOpConversionPattern : public OpConversionPattern<PrintOp> {
|
|
using OpConversionPattern<PrintOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(PrintOp op, OpAdaptor operands,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
|
|
// Printf's will be emitted to stdout (32'h8000_0001 in IEEE Std 1800-2012).
|
|
Value fdStdout =
|
|
hw::ConstantOp::create(rewriter, op.getLoc(), APInt(32, 0x80000001));
|
|
|
|
auto fstrOp =
|
|
dyn_cast_or_null<FormatVerilogStringOp>(op.getString().getDefiningOp());
|
|
if (!fstrOp)
|
|
return op->emitOpError() << "expected FormatVerilogStringOp as the "
|
|
"source of the formatted string";
|
|
|
|
rewriter.replaceOpWithNewOp<sv::FWriteOp>(
|
|
op, fdStdout, fstrOp.getFormatString(), fstrOp.getSubstitutions());
|
|
return success();
|
|
}
|
|
};
|
|
|
|
struct HasBeenResetConversion : public OpConversionPattern<HasBeenResetOp> {
|
|
using OpConversionPattern<HasBeenResetOp>::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(HasBeenResetOp op, OpAdaptor operands,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
auto i1 = rewriter.getI1Type();
|
|
auto constOne = hw::ConstantOp::create(rewriter, op.getLoc(), i1, 1);
|
|
auto constZero = hw::ConstantOp::create(rewriter, op.getLoc(), i1, 0);
|
|
auto constX = sv::ConstantXOp::create(rewriter, op.getLoc(), i1);
|
|
|
|
// Declare the register that will track the reset state.
|
|
auto reg = sv::RegOp::create(rewriter, op.getLoc(), i1,
|
|
rewriter.getStringAttr("hasBeenResetReg"));
|
|
|
|
auto clock = operands.getClock();
|
|
auto reset = operands.getReset();
|
|
|
|
// Explicitly initialize the register in an `initial` block. In general, the
|
|
// register will come up as X, but this may be overridden by simulator
|
|
// configuration options.
|
|
//
|
|
// In case the reset is async, check if the reset is already active during
|
|
// the `initial` block and immediately set the register to 1. Otherwise
|
|
// initialize to X.
|
|
sv::InitialOp::create(rewriter, op.getLoc(), [&] {
|
|
auto assignOne = [&] {
|
|
sv::BPAssignOp::create(rewriter, op.getLoc(), reg, constOne);
|
|
};
|
|
auto assignX = [&] {
|
|
sv::BPAssignOp::create(rewriter, op.getLoc(), reg, constX);
|
|
};
|
|
if (op.getAsync())
|
|
sv::IfOp::create(rewriter, op.getLoc(), reset, assignOne, assignX);
|
|
else
|
|
assignX();
|
|
});
|
|
|
|
// Create the `always` block that sets the register to 1 as soon as the
|
|
// reset is initiated. For async resets this happens at the reset's posedge;
|
|
// for sync resets this happens on the clock's posedge if the reset is set.
|
|
Value triggerOn = op.getAsync() ? reset : clock;
|
|
sv::AlwaysOp::create(
|
|
rewriter, op.getLoc(), sv::EventControl::AtPosEdge, triggerOn, [&] {
|
|
auto assignOne = [&] {
|
|
sv::PAssignOp::create(rewriter, op.getLoc(), reg, constOne);
|
|
};
|
|
if (op.getAsync())
|
|
assignOne();
|
|
else
|
|
sv::IfOp::create(rewriter, op.getLoc(), reset, assignOne);
|
|
});
|
|
|
|
// Derive the actual result value:
|
|
// hasBeenReset = (hasBeenResetReg === 1) && (reset === 0);
|
|
auto regRead = sv::ReadInOutOp::create(rewriter, op.getLoc(), reg);
|
|
auto regIsOne = rewriter.createOrFold<comb::ICmpOp>(
|
|
op.getLoc(), comb::ICmpPredicate::ceq, regRead, constOne);
|
|
auto resetIsZero = rewriter.createOrFold<comb::ICmpOp>(
|
|
op.getLoc(), comb::ICmpPredicate::ceq, reset, constZero);
|
|
auto resetStartedAndEnded = rewriter.createOrFold<comb::AndOp>(
|
|
op.getLoc(), regIsOne, resetIsZero, true);
|
|
rewriter.replaceOpWithNewOp<hw::WireOp>(
|
|
op, resetStartedAndEnded, rewriter.getStringAttr("hasBeenReset"));
|
|
|
|
return success();
|
|
}
|
|
};
|
|
|
|
// Conversion from one event control enum to another
|
|
static sv::EventControl verifToSVEventControl(verif::ClockEdge ce) {
|
|
switch (ce) {
|
|
case verif::ClockEdge::Pos:
|
|
return sv::EventControl::AtPosEdge;
|
|
case verif::ClockEdge::Neg:
|
|
return sv::EventControl::AtNegEdge;
|
|
case verif::ClockEdge::Both:
|
|
return sv::EventControl::AtEdge;
|
|
}
|
|
llvm_unreachable("Unknown event control kind");
|
|
}
|
|
|
|
// Generic conversion for verif.assert, verif.assume and verif.cover
|
|
template <typename Op, typename TargetOp>
|
|
struct VerifAssertLikeConversion : public OpConversionPattern<Op> {
|
|
using OpConversionPattern<Op>::OpConversionPattern;
|
|
using OpAdaptor = typename OpConversionPattern<Op>::OpAdaptor;
|
|
|
|
// Convert the verif.assert like op that uses an enable, into an equivalent
|
|
// sv.assert_property op that has the negated enable as its disable.
|
|
LogicalResult
|
|
matchAndRewrite(Op op, OpAdaptor operands,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
// Negate the enable if it exists
|
|
Value disable;
|
|
if (auto enable = operands.getEnable()) {
|
|
Value constOne = rewriter.createOrFold<hw::ConstantOp>(
|
|
op.getLoc(), rewriter.getI1Type(), 1);
|
|
disable =
|
|
rewriter.createOrFold<comb::XorOp>(op.getLoc(), enable, constOne);
|
|
}
|
|
|
|
rewriter.replaceOpWithNewOp<TargetOp>(op, operands.getProperty(), disable,
|
|
operands.getLabelAttr());
|
|
|
|
return success();
|
|
}
|
|
};
|
|
|
|
// Generic conversion for verif.clocked_assert, verif.clocked_assume and
|
|
// verif.clocked_cover
|
|
template <typename Op, typename TargetOp>
|
|
struct VerifClockedAssertLikeConversion : public OpConversionPattern<Op> {
|
|
using OpConversionPattern<Op>::OpConversionPattern;
|
|
using OpAdaptor = typename OpConversionPattern<Op>::OpAdaptor;
|
|
|
|
// Convert the verif.assert like op that uses an enable, into an equivalent
|
|
// sv.assert_property op that has the negated enable as its disable.
|
|
LogicalResult
|
|
matchAndRewrite(Op op, OpAdaptor operands,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
// Negate the enable if it exists
|
|
Value disable;
|
|
if (auto enable = operands.getEnable()) {
|
|
Value constOne = rewriter.createOrFold<hw::ConstantOp>(
|
|
op.getLoc(), rewriter.getI1Type(), 1);
|
|
disable =
|
|
rewriter.createOrFold<comb::XorOp>(op.getLoc(), enable, constOne);
|
|
}
|
|
// Convert a verif clock edge into an sv event control
|
|
auto eventattr = sv::EventControlAttr::get(
|
|
op.getContext(), verifToSVEventControl(operands.getEdge()));
|
|
|
|
rewriter.replaceOpWithNewOp<TargetOp>(op, operands.getProperty(), eventattr,
|
|
operands.getClock(), disable,
|
|
operands.getLabelAttr());
|
|
|
|
return success();
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Pass Infrastructure
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct VerifToSVPass : public circt::impl::LowerVerifToSVBase<VerifToSVPass> {
|
|
void runOnOperation() override;
|
|
};
|
|
} // namespace
|
|
|
|
void VerifToSVPass::runOnOperation() {
|
|
MLIRContext &context = getContext();
|
|
hw::HWModuleOp module = getOperation();
|
|
|
|
ConversionTarget target(context);
|
|
RewritePatternSet patterns(&context);
|
|
|
|
target.addIllegalOp<PrintOp, HasBeenResetOp, verif::AssertOp, verif::AssumeOp,
|
|
verif::CoverOp, ClockedAssertOp, ClockedAssumeOp,
|
|
ClockedCoverOp>();
|
|
target.addLegalDialect<sv::SVDialect, hw::HWDialect, comb::CombDialect>();
|
|
patterns.add<
|
|
PrintOpConversionPattern, HasBeenResetConversion,
|
|
VerifAssertLikeConversion<verif::AssertOp, AssertPropertyOp>,
|
|
VerifAssertLikeConversion<verif::AssumeOp, AssumePropertyOp>,
|
|
VerifAssertLikeConversion<verif::CoverOp, CoverPropertyOp>,
|
|
VerifClockedAssertLikeConversion<verif::ClockedAssertOp,
|
|
AssertPropertyOp>,
|
|
VerifClockedAssertLikeConversion<verif::ClockedAssumeOp,
|
|
AssumePropertyOp>,
|
|
VerifClockedAssertLikeConversion<verif::ClockedCoverOp, CoverPropertyOp>>(
|
|
&context);
|
|
|
|
if (failed(applyPartialConversion(module, target, std::move(patterns))))
|
|
signalPassFailure();
|
|
}
|
|
|
|
std::unique_ptr<OperationPass<hw::HWModuleOp>>
|
|
circt::createLowerVerifToSVPass() {
|
|
return std::make_unique<VerifToSVPass>();
|
|
}
|