Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ def LowerFIRRTLToHW : Pass<"lower-firrtl-to-hw", "mlir::ModuleOp"> {
"sim::SimDialect",
"sv::SVDialect",
"verif::VerifDialect",
"mlir::scf::SCFDialect",
];
let options = [
Option<"enableAnnotationWarning", "warn-on-unprocessed-annotations",
Expand Down
44 changes: 44 additions & 0 deletions include/circt/Dialect/Sim/SimTransforms.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===- SimTransforms.h - Sim transform helpers -----------------*- C++ -*-===//
//
// 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 header declares reusable transformation helpers for the Sim dialect.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_DIALECT_SIM_SIMTRANSFORMS_H
#define CIRCT_DIALECT_SIM_SIMTRANSFORMS_H

#include "mlir/IR/Builders.h"
#include "llvm/ADT/ArrayRef.h"

namespace circt {
namespace sim {

struct PrintProceduralizationRequest {
mlir::Location loc;
mlir::Value input;
mlir::Value condition;
mlir::Value stream;

/// Operation used for diagnostics, if any.
mlir::Operation *anchorOp = nullptr;

/// Operation to erase once the request has been proceduralized, if any.
mlir::Operation *cleanupRoot = nullptr;
};

/// Lower a list of same-clock print requests into a shared `hw.triggered`
/// region containing `sim.proc.print` operations.
mlir::LogicalResult proceduralizePrintsForClock(
mlir::OpBuilder &builder, mlir::Value clock,
llvm::ArrayRef<PrintProceduralizationRequest> printRequests);

} // namespace sim
} // namespace circt

#endif // CIRCT_DIALECT_SIM_SIMTRANSFORMS_H
1 change: 1 addition & 0 deletions lib/Conversion/FIRRTLToHW/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_circt_conversion_library(CIRCTFIRRTLToHW
CIRCTLTL
CIRCTSeq
CIRCTSim
CIRCTSimTransforms
CIRCTSV
CIRCTSVLoweringUtils
CIRCTVerif
Expand Down
103 changes: 75 additions & 28 deletions lib/Conversion/FIRRTLToHW/LowerToHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,18 @@
#include "circt/Dialect/SV/SVOps.h"
#include "circt/Dialect/Seq/SeqOps.h"
#include "circt/Dialect/Sim/SimOps.h"
#include "circt/Dialect/Sim/SimTransforms.h"
#include "circt/Dialect/Verif/VerifOps.h"
#include "circt/Support/BackedgeBuilder.h"
#include "circt/Support/Namespace.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/ImplicitLocOpBuilder.h"
#include "mlir/IR/Threading.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Path.h"
Expand Down Expand Up @@ -1755,6 +1758,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
Backedge createBackedge(Location loc, Type type);
Backedge createBackedge(Value orig, Type type);
bool updateIfBackedge(Value dest, Value src);
FailureOr<Value> resolveBackedge(Value root);

/// Returns true if the lowered operation requires an inner symbol on it.
bool requiresInnerSymbol(hw::InnerSymbolOpInterface op) {
Expand Down Expand Up @@ -2042,6 +2046,7 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
LogicalResult visitPrintfLike(T op,
const FileDescriptorInfo &fileDescriptorInfo,
bool usePrintfCond);
LogicalResult flushProceduralizedPrints();
LogicalResult visitStmt(PrintFOp op);
LogicalResult visitStmt(FPrintFOp op);
LogicalResult visitStmt(FFlushOp op);
Expand Down Expand Up @@ -2151,6 +2156,11 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
/// parent operation has handled the region, blocks, and block arguments.
SmallVector<std::pair<Block::iterator, Block::iterator>> worklist;

/// Lower-to-core print requests preserved in IR order until the end of the
/// lowering, where they are regrouped by their resolved clocks.
SmallVector<std::pair<Value, sim::PrintProceduralizationRequest>, 2>
pendingPrintRequests;

void addToWorklist(Block &block) {
worklist.push_back({block.begin(), block.end()});
}
Expand Down Expand Up @@ -2236,41 +2246,26 @@ LogicalResult FIRRTLLowering::run() {
}
}

if (failed(flushProceduralizedPrints())) {
backedgeBuilder.abandon();
return failure();
}

// Replace all backedges with uses of their regular values. We process them
// after the module body since the lowering table is too hard to keep up to
// date. Multiple operations may be lowered to the same backedge when values
// are folded, which means we would have to scan the entire lowering table to
// safely replace a backedge.
for (auto &[backedge, value] : backedges) {
SmallVector<Location> driverLocs;
// In the case where we have backedges connected to other backedges, we have
// to find the value that actually drives the group.
while (true) {
// If we find the original backedge we have some undriven logic or
// a combinatorial loop. Bail out and provide information on the nodes.
if (backedge == value) {
Location edgeLoc = backedge.getLoc();
if (driverLocs.empty()) {
mlir::emitError(edgeLoc, "sink does not have a driver");
} else {
auto diag = mlir::emitError(edgeLoc, "sink in combinational loop");
for (auto loc : driverLocs)
diag.attachNote(loc) << "through driver here";
}
backedgeBuilder.abandon();
return failure();
}
// If the value is not another backedge, we have found the driver.
auto *it = backedges.find(value);
if (it == backedges.end())
break;
// Find what is driving the next backedge.
driverLocs.push_back(value.getLoc());
value = it->second;
for (auto &[backedge, ignored] : backedges) {
(void)ignored;
auto resolvedValue = resolveBackedge(backedge);
if (failed(resolvedValue)) {
backedgeBuilder.abandon();
return failure();
}
if (auto *defOp = backedge.getDefiningOp())
maybeUnusedValues.erase(defOp);
backedge.replaceAllUsesWith(value);
backedge.replaceAllUsesWith(*resolvedValue);
}

// Now that all of the operations that can be lowered are, remove th
Expand Down Expand Up @@ -3146,6 +3141,38 @@ bool FIRRTLLowering::updateIfBackedge(Value dest, Value src) {
return true;
}

FailureOr<Value> FIRRTLLowering::resolveBackedge(Value root) {
if (!root)
return root;

DenseSet<Value> visited;
SmallVector<Location> driverLocs;
Value current = root;
while (true) {
auto *it = backedges.find(current);
if (it == backedges.end())
return current;

Value next = it->second;
if (next == current) {
auto diag = mlir::emitError(root.getLoc(), "sink does not have a driver");
for (auto loc : driverLocs)
diag.attachNote(loc) << "through driver here";
return failure();
}

if (next == root || !visited.insert(next).second) {
auto diag = mlir::emitError(root.getLoc(), "sink in combinational loop");
for (auto loc : driverLocs)
diag.attachNote(loc) << "through driver here";
return failure();
}

driverLocs.push_back(next.getLoc());
current = next;
}
}

/// Switch the insertion point of the current builder to the end of the
/// specified block and run the closure. This correctly handles the case
/// where the closure is null, but the caller needs to make sure the block
Expand Down Expand Up @@ -3940,6 +3967,25 @@ LogicalResult FIRRTLLowering::visitDecl(MemOp op) {
return success();
}

LogicalResult FIRRTLLowering::flushProceduralizedPrints() {
SmallMapVector<Value, SmallVector<sim::PrintProceduralizationRequest>, 2>
printRequestMap;
for (auto &[clock, request] : pendingPrintRequests) {
auto resolvedClock = resolveBackedge(clock);
if (failed(resolvedClock))
return failure();
printRequestMap[*resolvedClock].push_back(request);
}

for (auto &[clock, requests] : printRequestMap) {
OpBuilder printBuilder(requests.back().anchorOp);
if (failed(sim::proceduralizePrintsForClock(printBuilder, clock, requests)))
return failure();
}
pendingPrintRequests.clear();
return success();
}

LogicalResult
FIRRTLLowering::prepareInstanceOperands(ArrayRef<PortInfo> portInfo,
Operation *instanceOp,
Expand Down Expand Up @@ -5457,7 +5503,8 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
if (failed(formatString))
return failure();

sim::PrintFormattedOp::create(builder, *formatString, clock, cond);
pendingPrintRequests.push_back(
{clock, {op.getLoc(), *formatString, cond, Value(), op, nullptr}});
return success();
}

Expand Down
Loading
Loading