Skip to content

Commit

Permalink
[MooreToCore] Add always_comb and always_latch lowering support
Browse files Browse the repository at this point in the history
  • Loading branch information
maerhart committed Aug 20, 2024
1 parent fcbd718 commit c2f747a
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 59 deletions.
124 changes: 65 additions & 59 deletions lib/Conversion/MooreToCore/MooreToCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/Iterators.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/DialectConversion.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/ADT/TypeSwitch.h"

namespace circt {
Expand Down Expand Up @@ -103,30 +105,6 @@ static hw::ModulePortInfo getModulePortInfo(const TypeConverter &typeConverter,
return hw::ModulePortInfo(inputs, outputs);
}

/// Erase all dead blocks in a region.
static void eraseDeadBlocks(PatternRewriter &rewriter, Region &region) {
SmallVector<Block *> worklist;
for (auto &block : llvm::make_early_inc_range(llvm::drop_begin(region, 1))) {
if (!block.use_empty())
continue;
worklist.push_back(&block);
while (!worklist.empty()) {
auto *block = worklist.pop_back_val();
if (!block->use_empty())
continue;
for (auto *successor : block->getSuccessors())
worklist.push_back(successor);
rewriter.eraseBlock(block);
}
}
}

/// Erase all dead blocks in an op.
static void eraseDeadBlocks(PatternRewriter &rewriter, Operation *op) {
for (auto &region : op->getRegions())
eraseDeadBlocks(rewriter, region);
}

//===----------------------------------------------------------------------===//
// Structural Conversion
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -188,16 +166,63 @@ struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
}
};

static void getValuesToObserve(Region *region,
function_ref<void(Value)> setInsertionPoint,
const TypeConverter *typeConverter,
ConversionPatternRewriter &rewriter,
SmallVector<Value> &observeValues) {
SmallDenseSet<Value> alreadyObserved;
Location loc = region->getLoc();

auto probeIfSignal = [&](Value value) -> Value {
if (!isa<hw::InOutType>(value.getType()))
return value;
return rewriter.create<llhd::PrbOp>(loc, value);
};

region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
[&](Operation *operation) {
for (auto value : operation->getOperands()) {
if (region->isAncestor(value.getParentRegion()))
continue;
if (!alreadyObserved.insert(value).second)
continue;

OpBuilder::InsertionGuard g(rewriter);
if (auto remapped = rewriter.getRemappedValue(value)) {
setInsertionPoint(remapped);
observeValues.push_back(probeIfSignal(remapped));
} else {
setInsertionPoint(value);
auto type = typeConverter->convertType(value.getType());
auto converted = typeConverter->materializeTargetConversion(
rewriter, loc, type, value);
observeValues.push_back(probeIfSignal(converted));
}
}
});
}

struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
using OpConversionPattern::OpConversionPattern;

LogicalResult
matchAndRewrite(ProcedureOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
// Collect values to observe before we do any modifications to the region.
SmallVector<Value> observedValues;
if (op.getKind() == ProcedureKind::AlwaysComb ||
op.getKind() == ProcedureKind::AlwaysLatch) {
auto setInsertionPoint = [&](Value value) {
rewriter.setInsertionPoint(op);
};
getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter,
rewriter, observedValues);
}

auto loc = op.getLoc();
if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter)))
return failure();
eraseDeadBlocks(rewriter, op);

// Handle initial and final procedures. These lower to a corresponding
// `llhd.process` or `llhd.final` op that executes the body and then halts.
Expand Down Expand Up @@ -239,9 +264,10 @@ struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
// jumping back up to the body.
if (op.getKind() == ProcedureKind::AlwaysComb ||
op.getKind() == ProcedureKind::AlwaysLatch) {
block = rewriter.createBlock(&newOp.getBody());
// TODO: Collect observed signals and add LLHD wait op.
return failure();
Block *waitBlock = rewriter.createBlock(&newOp.getBody());
rewriter.create<llhd::WaitOp>(loc, observedValues, Value(), ValueRange{},
block);
block = waitBlock;
}

// Make all `moore.return` ops branch back up to the beginning of the
Expand Down Expand Up @@ -322,44 +348,19 @@ struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
rewriter.eraseOp(detectOp);
}

// Determine the values used during event detection that are defined outside
// the `wait_event`'s body region. We want to wait for a change on these
// signals before we check if any interesting event happened.
SmallVector<Value> observeValues;
auto setInsertionPointAfterDef = [&](Value value) {
if (auto *op = value.getDefiningOp())
rewriter.setInsertionPointAfter(op);
if (auto arg = dyn_cast<BlockArgument>(value))
rewriter.setInsertionPointToStart(value.getParentBlock());
};

auto probeIfSignal = [&](Value value) -> Value {
if (!isa<hw::InOutType>(value.getType()))
return value;
return rewriter.create<llhd::PrbOp>(loc, value);
};

// Determine the values used during event detection that are defined outside
// the `wait_event`'s body region. We want to wait for a change on these
// signals before we check if any interesting event happened.
SmallVector<Value> observeValues;
SmallDenseSet<Value> alreadyObserved;
clonedOp.getBody().walk([&](Operation *operation) {
for (auto value : operation->getOperands()) {
if (clonedOp.getBody().isAncestor(value.getParentRegion()))
continue;
if (!alreadyObserved.insert(value).second)
continue;
if (auto remapped = rewriter.getRemappedValue(value)) {
OpBuilder::InsertionGuard g(rewriter);
setInsertionPointAfterDef(remapped);
observeValues.push_back(probeIfSignal(remapped));
} else {
OpBuilder::InsertionGuard g(rewriter);
setInsertionPointAfterDef(value);
auto type = typeConverter->convertType(value.getType());
auto converted = typeConverter->materializeTargetConversion(
rewriter, loc, type, value);
observeValues.push_back(probeIfSignal(converted));
}
}
});
getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef,
typeConverter, rewriter, observeValues);

// Create the `llhd.wait` op that suspends the current process and waits for
// a change in the interesting values listed in `observeValues`. When a
Expand Down Expand Up @@ -1388,6 +1389,11 @@ void MooreToCorePass::runOnOperation() {
MLIRContext &context = getContext();
ModuleOp module = getOperation();

IRRewriter rewriter(module);
module->walk([&](Operation *op) {
(void)mlir::eraseUnreachableBlocks(rewriter, op->getRegions());
});

ConversionTarget target(context);
TypeConverter typeConverter;
RewritePatternSet patterns(&context);
Expand Down
74 changes: 74 additions & 0 deletions test/Conversion/MooreToCore/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,45 @@ moore.module @WaitEvent() {
moore.return
}

// CHECK: [[FALSE:%.+]] = hw.constant false
// CHECK: [[PRB_A:%.+]] = llhd.prb %a
// CHECK: [[PRB_B:%.+]] = llhd.prb %b
// CHECK: llhd.process {
%cond = moore.constant 0 : i1
moore.procedure always_comb {
// CHECK: cf.br ^[[BB1:.+]]
// CHECK: ^[[BB1]]:
// CHECK: llhd.prb %a
// CHECK: llhd.prb %b
// CHECK: cf.br ^[[BB2:.+]]
// CHECK: ^[[BB2]]:
// CHECK: llhd.wait ([[FALSE]], [[PRB_A]], [[PRB_B]] : {{.*}}), ^[[BB1]]
%1 = moore.conditional %cond : i1 -> i1 {
%2 = moore.read %a : <i1>
moore.yield %2 : !moore.i1
} {
%3 = moore.read %b : <i1>
moore.yield %3 : !moore.i1
}
moore.return
}

// CHECK: [[PRB_D:%.+]] = llhd.prb %d
// CHECK: llhd.process {
// CHECK: cf.br ^[[BB1:.+]]
// CHECK: ^[[BB1]]:
// CHECK: llhd.prb %d
// CHECK: cf.br ^[[BB2:.+]]
// CHECK: ^[[BB2]]:
// CHECK: llhd.wait ([[PRB_D]] : {{.*}}), ^[[BB1]]
moore.procedure always_latch {
%3 = moore.read %d : <i1>
moore.return
}

// CHECK: %d = llhd.sig %false
%d = moore.variable : <i1>

// CHECK: llhd.process {
moore.procedure initial {
// CHECK: llhd.wait ([[PRB_A6]], [[PRB_B2]] :
Expand All @@ -708,3 +747,38 @@ moore.module @WaitEvent() {
moore.return
}
}

// Just check that block without predecessors are handled without crashing
// CHECK-LABEL: @NoPredecessorBlockErasure
moore.module @NoPredecessorBlockErasure(in %clk_i : !moore.l1, in %raddr_i : !moore.array<2 x l5>, out rdata_o : !moore.array<2 x l32>, in %waddr_i : !moore.array<1 x l5>, in %wdata_i : !moore.array<1 x l32>, in %we_i : !moore.l1) {
%0 = moore.constant 0 : l32
%1 = moore.constant 1 : i32
%2 = moore.constant 0 : i32
%rdata_o = moore.variable : <array<2 x l32>>
%mem = moore.variable : <array<32 x l32>>
moore.procedure always_ff {
cf.br ^bb1(%2 : !moore.i32)
^bb1(%4: !moore.i32): // 2 preds: ^bb0, ^bb8
moore.return
^bb2: // no predecessors
cf.br ^bb3(%2 : !moore.i32)
^bb3(%5: !moore.i32): // 2 preds: ^bb2, ^bb6
cf.br ^bb8
^bb4: // no predecessors
cf.br ^bb6
^bb5: // no predecessors
cf.br ^bb6
^bb6: // 2 preds: ^bb4, ^bb5
%6 = moore.add %5, %1 : i32
cf.br ^bb3(%6 : !moore.i32)
^bb7: // no predecessors
%7 = moore.extract_ref %mem from 0 : <array<32 x l32>> -> <l32>
moore.nonblocking_assign %7, %0 : l32
cf.br ^bb8
^bb8: // 2 preds: ^bb3, ^bb7
%8 = moore.add %4, %1 : i32
cf.br ^bb1(%8 : !moore.i32)
}
%3 = moore.read %rdata_o : <array<2 x l32>>
moore.output %3 : !moore.array<2 x l32>
}

0 comments on commit c2f747a

Please sign in to comment.