mirror of https://github.com/llvm/circt.git
This commit is contained in:
parent
84d73b94aa
commit
273439ec61
|
@ -516,7 +516,7 @@ def ConvertMooreToCore : Pass<"convert-moore-to-core", "mlir::ModuleOp"> {
|
|||
}];
|
||||
let constructor = "circt::createConvertMooreToCorePass()";
|
||||
let dependentDialects = ["comb::CombDialect", "hw::HWDialect",
|
||||
"llhd::LLHDDialect"];
|
||||
"llhd::LLHDDialect", "mlir::cf::ControlFlowDialect"];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -15,4 +15,5 @@ add_circt_conversion_library(CIRCTMooreToCore
|
|||
MLIRControlFlowDialect
|
||||
MLIRFuncDialect
|
||||
MLIRTransforms
|
||||
MLIRSideEffectInterfaces
|
||||
)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/IR/BuiltinDialect.h"
|
||||
#include "mlir/Interfaces/SideEffectInterfaces.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "mlir/Transforms/DialectConversion.h"
|
||||
#include "llvm/ADT/TypeSwitch.h"
|
||||
|
@ -148,6 +149,152 @@ struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
|
|||
}
|
||||
};
|
||||
|
||||
struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
|
||||
using OpConversionPattern::OpConversionPattern;
|
||||
|
||||
LogicalResult
|
||||
matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
Location loc = op.getLoc();
|
||||
auto procOp = rewriter.create<llhd::ProcessOp>(loc);
|
||||
|
||||
// TODO: properly handle the procedure kind attribute
|
||||
if (op.getKind() != ProcedureKind::Always)
|
||||
return rewriter.notifyMatchFailure(loc, "not yet supported");
|
||||
|
||||
// Collect all event ops in the procedure.
|
||||
SmallVector<EventOp> events(op.getOps<EventOp>());
|
||||
|
||||
auto *entry = rewriter.createBlock(&procOp.getBody());
|
||||
auto *wait = rewriter.createBlock(&procOp.getBody());
|
||||
auto *check = rewriter.createBlock(&procOp.getBody());
|
||||
|
||||
// We need to add an empty entry block because it is not allowed in MLIR to
|
||||
// branch back to the entry block. Instead we put the logic in the second
|
||||
// block and branch to that.
|
||||
rewriter.setInsertionPointToStart(entry);
|
||||
rewriter.create<cf::BranchOp>(loc, wait);
|
||||
|
||||
// The block in which we can sample the past and where the wait terminator
|
||||
// resides.
|
||||
rewriter.setInsertionPointToStart(wait);
|
||||
|
||||
auto getSignal = [&](Value input) -> Value {
|
||||
// If the read op input is defined outside and before the procedure
|
||||
// operation, we can get the remapped value directly.
|
||||
Value signal = rewriter.getRemappedValue(input);
|
||||
|
||||
// Otherwise, it hasn't been converted yet, so we take the old one and
|
||||
// insert a cast.
|
||||
if (!signal) {
|
||||
Type convertedType = typeConverter->convertType(input.getType());
|
||||
assert(convertedType &&
|
||||
"if the input has not been converted yet, it should have a "
|
||||
"moore type and a valid type conversion");
|
||||
signal =
|
||||
rewriter
|
||||
.create<UnrealizedConversionCastOp>(loc, convertedType, input)
|
||||
->getResult(0);
|
||||
}
|
||||
|
||||
return signal;
|
||||
};
|
||||
|
||||
// All signals to observe in the `llhd.wait` operation.
|
||||
SmallVector<Value> toObserve;
|
||||
DenseSet<Value> alreadyObserved;
|
||||
// If there are no event operations in the procedure, it's a combinational
|
||||
// one. Thus we need to collect all signals used.
|
||||
if (events.empty()) {
|
||||
op->walk([&](Operation *operation) {
|
||||
for (auto &operand : operation->getOpOperands()) {
|
||||
Value value = getSignal(operand.get());
|
||||
auto memOp = dyn_cast<MemoryEffectOpInterface>(operation);
|
||||
if (!memOp)
|
||||
return;
|
||||
|
||||
// The process is only sensitive to values that are read.
|
||||
if (isa<RefType>(operand.get().getType()) &&
|
||||
memOp.getEffectOnValue<MemoryEffects::Read>(operand.get())
|
||||
.has_value()) {
|
||||
if (!alreadyObserved.contains(value))
|
||||
toObserve.push_back(value);
|
||||
|
||||
alreadyObserved.insert(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Forall edge triggered events, probe the old value
|
||||
SmallVector<Value> oldValues(events.size(), Value());
|
||||
for (auto [i, event] : llvm::enumerate(events)) {
|
||||
auto readOp = event.getInput().getDefiningOp<ReadOp>();
|
||||
if (!readOp)
|
||||
return failure();
|
||||
|
||||
Value signal = getSignal(readOp.getInput());
|
||||
toObserve.push_back(signal);
|
||||
|
||||
// Non-edge triggered events only need the value in the present
|
||||
if (event.getEdge() != Edge::None)
|
||||
oldValues[i] = rewriter.create<llhd::PrbOp>(loc, signal);
|
||||
}
|
||||
|
||||
rewriter.create<llhd::WaitOp>(loc, toObserve, Value(), ValueRange{}, check);
|
||||
rewriter.setInsertionPointToStart(check);
|
||||
|
||||
if (events.empty()) {
|
||||
rewriter.create<cf::BranchOp>(loc, &op.getBody().front());
|
||||
} else {
|
||||
SmallVector<Value> disjuncts;
|
||||
for (auto [i, signal, event] : llvm::enumerate(toObserve, events)) {
|
||||
if (event.getEdge() == Edge::None)
|
||||
disjuncts.push_back(rewriter.create<llhd::PrbOp>(loc, signal));
|
||||
|
||||
if (event.getEdge() == Edge::PosEdge ||
|
||||
event.getEdge() == Edge::BothEdges) {
|
||||
Value currVal = rewriter.create<llhd::PrbOp>(loc, signal);
|
||||
Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
|
||||
Value notOldVal =
|
||||
rewriter.create<comb::XorOp>(loc, oldValues[i], trueVal);
|
||||
Value posedge = rewriter.create<comb::AndOp>(loc, notOldVal, currVal);
|
||||
disjuncts.push_back(posedge);
|
||||
}
|
||||
if (event.getEdge() == Edge::NegEdge ||
|
||||
event.getEdge() == Edge::BothEdges) {
|
||||
Value currVal = rewriter.create<llhd::PrbOp>(loc, signal);
|
||||
Value trueVal = rewriter.create<hw::ConstantOp>(loc, APInt(1, 1));
|
||||
Value notCurrVal =
|
||||
rewriter.create<comb::XorOp>(loc, currVal, trueVal);
|
||||
Value posedge =
|
||||
rewriter.create<comb::AndOp>(loc, oldValues[i], notCurrVal);
|
||||
disjuncts.push_back(posedge);
|
||||
}
|
||||
}
|
||||
|
||||
Value isValid = rewriter.create<comb::OrOp>(loc, disjuncts, false);
|
||||
rewriter.create<cf::CondBranchOp>(loc, isValid, &op.getBody().front(),
|
||||
wait);
|
||||
}
|
||||
|
||||
for (auto event : events)
|
||||
rewriter.eraseOp(event);
|
||||
|
||||
rewriter.inlineRegionBefore(op.getBody(), procOp.getBody(),
|
||||
procOp.getBody().end());
|
||||
|
||||
for (auto returnOp : procOp.getOps<ReturnOp>()) {
|
||||
rewriter.setInsertionPoint(returnOp);
|
||||
rewriter.create<cf::BranchOp>(loc, wait);
|
||||
rewriter.eraseOp(returnOp);
|
||||
}
|
||||
|
||||
rewriter.eraseOp(op);
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Declaration Conversion
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -617,14 +764,12 @@ struct ReadOpConversion : public OpConversionPattern<ReadOp> {
|
|||
LogicalResult
|
||||
matchAndRewrite(ReadOp op, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
Type resultType = typeConverter->convertType(op.getResult().getType());
|
||||
rewriter.replaceOpWithNewOp<llhd::PrbOp>(op, resultType,
|
||||
adaptor.getInput());
|
||||
rewriter.replaceOpWithNewOp<llhd::PrbOp>(op, adaptor.getInput());
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OpTy>
|
||||
template <typename OpTy, unsigned DeltaTime, unsigned EpsilonTime>
|
||||
struct AssignOpConversion : public OpConversionPattern<OpTy> {
|
||||
using OpConversionPattern<OpTy>::OpConversionPattern;
|
||||
using OpAdaptor = typename OpTy::Adaptor;
|
||||
|
@ -634,9 +779,8 @@ struct AssignOpConversion : public OpConversionPattern<OpTy> {
|
|||
ConversionPatternRewriter &rewriter) const override {
|
||||
// TODO: When we support delay control in Moore dialect, we need to update
|
||||
// this conversion.
|
||||
auto timeAttr =
|
||||
llhd::TimeAttr::get(op->getContext(), unsigned(0),
|
||||
llvm::StringRef("ns"), unsigned(0), unsigned(0));
|
||||
auto timeAttr = llhd::TimeAttr::get(
|
||||
op->getContext(), 0U, llvm::StringRef("ns"), DeltaTime, EpsilonTime);
|
||||
auto time = rewriter.create<llhd::ConstantTimeOp>(op->getLoc(), timeAttr);
|
||||
rewriter.replaceOpWithNewOp<llhd::DrvOp>(op, adaptor.getDst(),
|
||||
adaptor.getSrc(), time, Value{});
|
||||
|
@ -794,13 +938,15 @@ static void populateOpConversion(RewritePatternSet &patterns,
|
|||
ICmpOpConversion<WildcardNeOp, ICmpPredicate::wne>,
|
||||
|
||||
// Patterns of structural operations.
|
||||
SVModuleOpConversion, InstanceOpConversion,
|
||||
SVModuleOpConversion, InstanceOpConversion, ProcedureOpConversion,
|
||||
|
||||
// Patterns of shifting operations.
|
||||
ShrOpConversion, ShlOpConversion, AShrOpConversion,
|
||||
|
||||
// Patterns of assignment operations.
|
||||
AssignOpConversion<ContinuousAssignOp>,
|
||||
AssignOpConversion<ContinuousAssignOp, 0, 1>,
|
||||
AssignOpConversion<BlockingAssignOp, 0, 1>,
|
||||
AssignOpConversion<NonBlockingAssignOp, 1, 0>,
|
||||
|
||||
// Patterns of branch operations.
|
||||
CondBranchOpConversion, BranchOpConversion,
|
||||
|
|
|
@ -304,7 +304,7 @@ moore.module @Variable() {
|
|||
// CHECK: [[TMP2:%.+]] = hw.constant 10 : i32
|
||||
%3 = moore.constant 10 : i32
|
||||
|
||||
// CHECK: [[TIME:%.+]] = llhd.constant_time <0ns, 0d, 0e>
|
||||
// CHECK: [[TIME:%.+]] = llhd.constant_time <0ns, 0d, 1e>
|
||||
// CHECK: llhd.drv [[A]], [[TMP2]] after [[TIME]] : !hw.inout<i32>
|
||||
moore.assign %a, %3 : i32
|
||||
|
||||
|
@ -329,3 +329,131 @@ moore.module @Struct(in %arg0 : !moore.struct<{exp_bits: i32, man_bits: i32}>, o
|
|||
|
||||
moore.output %0, %3, %4 : !moore.i32, !moore.struct<{exp_bits: i32, man_bits: i32}>, !moore.struct<{exp_bits: i32, man_bits: i32}>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: hw.module @Process
|
||||
moore.module @Process(in %cond : i1) {
|
||||
// CHECK: [[B:%.+]] = llhd.sig "b"
|
||||
// CHECK: [[C:%.+]] = llhd.sig "c"
|
||||
// CHECK: [[D:%.+]] = llhd.sig "d"
|
||||
// CHECK: [[E:%.+]] = llhd.sig "e"
|
||||
%b = moore.variable : <i1>
|
||||
%c = moore.variable : <i1>
|
||||
%d = moore.variable : <i1>
|
||||
%e = moore.variable : <i1>
|
||||
|
||||
// CHECK: llhd.process
|
||||
moore.procedure always {
|
||||
// CHECK: cf.br ^[[BB1:.+]]
|
||||
// CHECK: ^[[BB1]]:
|
||||
// CHECK: [[C_OLD:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: llhd.wait ([[B]], [[C]] : !hw.inout<i1>, !hw.inout<i1>), ^[[BB2:.+]]
|
||||
// CHECK: ^[[BB2]]:
|
||||
// CHECK: [[B_CURR:%.+]] = llhd.prb [[B]]
|
||||
// CHECK: [[C_CURR:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[NOT_C_OLD:%.+]] = comb.xor [[C_OLD]], %true
|
||||
// CHECK: [[POSEDGE:%.+]] = comb.and [[NOT_C_OLD]], [[C_CURR]] : i1
|
||||
// CHECK: [[PROCEED:%.+]] = comb.or [[B_CURR]], [[POSEDGE]] : i1
|
||||
// CHECK: cf.cond_br [[PROCEED]], ^[[BB3:.+]], ^[[BB1]]
|
||||
// CHECK: ^[[BB3]]:
|
||||
// CHECK: [[B_PRB:%.+]] = llhd.prb [[B]]
|
||||
// CHECK: [[C_PRB:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[RES:%.+]] = comb.add [[B_PRB]], [[C_PRB]] : i1
|
||||
// CHECK: [[T0:%.+]] = llhd.constant_time <0ns, 1d, 0e>
|
||||
// CHECK: llhd.drv [[D]], [[RES]] after [[T0]]
|
||||
// CHECK: [[T1:%.+]] = llhd.constant_time <0ns, 0d, 1e>
|
||||
// CHECK: llhd.drv [[E]], [[RES]] after [[T1]]
|
||||
// CHECK: cf.br ^[[BB1]]
|
||||
%br = moore.read %b : <i1>
|
||||
moore.wait_event none %br : i1
|
||||
%cr = moore.read %c : <i1>
|
||||
moore.wait_event posedge %cr : i1
|
||||
|
||||
%0 = moore.add %br, %cr : i1
|
||||
|
||||
moore.nonblocking_assign %d, %0 : i1
|
||||
moore.blocking_assign %e, %0 : i1
|
||||
moore.return
|
||||
}
|
||||
|
||||
// CHECK: llhd.process
|
||||
moore.procedure always {
|
||||
// CHECK: cf.br ^[[BB1:.+]]
|
||||
// CHECK: ^[[BB1]]:
|
||||
// CHECK: llhd.wait ([[B]], [[C]] : !hw.inout<i1>, !hw.inout<i1>), ^[[BB2:.+]]
|
||||
// CHECK: ^[[BB2]]:
|
||||
// CHECK: cf.br ^[[BB3:.+]]
|
||||
// CHECK: ^[[BB3]]:
|
||||
// CHECK: [[B_PRB:%.+]] = llhd.prb [[B]]
|
||||
// CHECK: [[C_PRB:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[RES:%.+]] = comb.add [[B_PRB]], [[C_PRB]] : i1
|
||||
// CHECK: [[T0:%.+]] = llhd.constant_time <0ns, 1d, 0e>
|
||||
// CHECK: llhd.drv [[D]], [[RES]] after [[T0]]
|
||||
// CHECK: cf.br ^[[BB1]]
|
||||
%br = moore.read %b : <i1>
|
||||
%cr = moore.read %c : <i1>
|
||||
%0 = moore.add %br, %cr : i1
|
||||
moore.nonblocking_assign %d, %0 : i1
|
||||
moore.return
|
||||
}
|
||||
|
||||
// CHECK: llhd.process
|
||||
moore.procedure always {
|
||||
// CHECK: cf.br ^[[BB1:.+]]
|
||||
// CHECK: ^[[BB1]]:
|
||||
// CHECK: [[C_OLD:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: llhd.wait ([[C]] : !hw.inout<i1>), ^[[BB2:.+]]
|
||||
// CHECK: ^[[BB2]]:
|
||||
// CHECK: [[C_CURR:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[NOT_C_OLD:%.+]] = comb.xor [[C_OLD]], %true
|
||||
// CHECK: [[POSEDGE:%.+]] = comb.and [[NOT_C_OLD]], [[C_CURR]] : i1
|
||||
// CHECK: [[C_CURR1:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[NOT_C_CURR:%.+]] = comb.xor [[C_CURR1]], %true
|
||||
// CHECK: [[NEGEDGE:%.+]] = comb.and [[C_OLD]], [[NOT_C_CURR]] : i1
|
||||
// CHECK: [[PROCEED:%.+]] = comb.or [[POSEDGE]], [[NEGEDGE]] : i1
|
||||
// CHECK: cf.cond_br [[PROCEED]], ^[[BB3:.+]], ^[[BB1]]
|
||||
// CHECK: ^[[BB3]]:
|
||||
// CHECK: [[RES:%.+]] = comb.add [[B_PRB:%.+]], [[C_PRB:%.+]] : i1
|
||||
// CHECK: [[T0:%.+]] = llhd.constant_time <0ns, 1d, 0e>
|
||||
// CHECK: llhd.drv [[D]], [[RES]] after [[T0]]
|
||||
// CHECK: cf.br ^[[BB1]]
|
||||
moore.wait_event edge %cr : i1
|
||||
%0 = moore.add %br, %cr : i1
|
||||
moore.nonblocking_assign %d, %0 : i1
|
||||
moore.return
|
||||
}
|
||||
|
||||
// CHECK: [[B_PRB]] = llhd.prb [[B]]
|
||||
// CHECK: [[C_PRB]] = llhd.prb [[C]]
|
||||
%br = moore.read %b : <i1>
|
||||
%cr = moore.read %c : <i1>
|
||||
// CHECK: llhd.process
|
||||
moore.procedure always {
|
||||
// CHECK: cf.br ^[[BB1:.+]]
|
||||
// CHECK: ^[[BB1]]:
|
||||
// CHECK: [[C_OLD:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: llhd.wait ([[C]] : !hw.inout<i1>), ^[[BB2:.+]]
|
||||
// CHECK: ^[[BB2]]:
|
||||
// CHECK: [[C_CURR:%.+]] = llhd.prb [[C]]
|
||||
// CHECK: [[NOT_C_CURR:%.+]] = comb.xor [[C_CURR]], %true
|
||||
// CHECK: [[NEGEDGE:%.+]] = comb.and [[C_OLD]], [[NOT_C_CURR]] : i1
|
||||
// CHECK: [[PROCEED:%.+]] = comb.or [[NEGEDGE]] : i1
|
||||
// CHECK: cf.cond_br [[PROCEED]], ^[[BB3:.+]], ^[[BB1]]
|
||||
// CHECK: ^[[BB3]]:
|
||||
// CHECK: [[RES:%.+]] = comb.add [[B_PRB]], [[C_PRB]] : i1
|
||||
// CHECK: cf.cond_br %cond, ^[[BB4:.+]], ^[[BB5:.+]]
|
||||
// CHECK: ^[[BB4]]:
|
||||
// CHECK: [[T0:%.+]] = llhd.constant_time <0ns, 1d, 0e>
|
||||
// CHECK: llhd.drv [[D]], [[RES]] after [[T0]]
|
||||
// CHECK: cf.br ^[[BB1]]
|
||||
// CHECK: ^[[BB5]]:
|
||||
// CHECK: cf.br ^[[BB1]]
|
||||
moore.wait_event negedge %cr : i1
|
||||
%0 = moore.add %br, %cr : i1
|
||||
cf.cond_br %cond, ^bb1, ^bb2
|
||||
^bb1:
|
||||
moore.nonblocking_assign %d, %0 : i1
|
||||
moore.return
|
||||
^bb2:
|
||||
moore.return
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue