Skip to content
Open
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 @@ -429,6 +429,7 @@ def LowerFIRRTLToHW : Pass<"lower-firrtl-to-hw", "mlir::ModuleOp"> {
"emit::EmitDialect",
"hw::HWDialect",
"ltl::LTLDialect",
"mlir::scf::SCFDialect",
"seq::SeqDialect",
"sim::SimDialect",
"sv::SVDialect",
Expand Down
1 change: 1 addition & 0 deletions include/circt/Dialect/Sim/SimOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,7 @@ def TriggeredOp : SimOp<"triggered", [

let arguments = (ins ClockType:$clock, Optional<I1>:$condition);
let regions = (region SizedRegion<1>:$body);
let hasCanonicalizeMethod = true;
let assemblyFormat = [{
$clock (`if` $condition^)? $body attr-dict
}];
Expand Down
1 change: 1 addition & 0 deletions lib/Conversion/FIRRTLToHW/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ add_circt_conversion_library(CIRCTFIRRTLToHW
CIRCTSV
CIRCTSVLoweringUtils
CIRCTVerif
MLIRSCFDialect
MLIRTransforms
)
77 changes: 73 additions & 4 deletions lib/Conversion/FIRRTLToHW/LowerToHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#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"
Expand Down Expand Up @@ -1808,6 +1809,8 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
sv::EventControl(), Value(), body,
std::function<void(void)>());
}
void addToSimTriggeredBlock(Value clock, Value condition,
const std::function<void(void)> &body = {});

LogicalResult emitGuards(Location loc, ArrayRef<Attribute> guards,
std::function<void(void)> emit);
Expand Down Expand Up @@ -2107,6 +2110,10 @@ struct FIRRTLLowering : public FIRRTLVisitor<FIRRTLLowering, LogicalResult> {
sv::ResetType, sv::EventControl, Value>;
llvm::SmallDenseMap<AlwaysKeyType, std::pair<sv::AlwaysOp, sv::IfOp>>
alwaysBlocks;
llvm::SmallDenseMap<std::pair<Block *, Value>, sim::TriggeredOp>
simTriggeredBlocks;
llvm::SmallDenseMap<sim::TriggeredOp, std::pair<Value, mlir::scf::IfOp>>
simTriggeredLastConditionBlocks;
llvm::SmallDenseMap<std::pair<Block *, Attribute>, sv::IfDefOp> ifdefBlocks;
llvm::SmallDenseMap<Block *, sv::InitialOp> initialBlocks;

Expand Down Expand Up @@ -3271,6 +3278,48 @@ void FIRRTLLowering::addToAlwaysBlock(
builder.getInsertionPoint());
}

void FIRRTLLowering::addToSimTriggeredBlock(
Value clock, Value condition, const std::function<void(void)> &body) {
auto key = std::make_pair(builder.getBlock(), clock);
auto triggered = simTriggeredBlocks.lookup(key);
if (!triggered) {
triggered = sim::TriggeredOp::create(builder, clock);
simTriggeredBlocks[key] = triggered;
}

if (condition) {
auto &lastConditionBlock = simTriggeredLastConditionBlocks[triggered];
auto ifOp = lastConditionBlock.first == condition
? lastConditionBlock.second
: mlir::scf::IfOp();
if (!ifOp) {
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(triggered.getBodyBlock());
ifOp = mlir::scf::IfOp::create(builder, builder.getLoc(), TypeRange{},
condition, true, false);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
mlir::scf::YieldOp::create(builder, builder.getLoc());
lastConditionBlock = {condition, ifOp};
}

OpBuilder::InsertionGuard guard(builder);
auto &thenBlock = ifOp.getThenRegion().front();
builder.setInsertionPoint(thenBlock.getTerminator());
body();
} else {
OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(triggered.getBodyBlock());
body();
simTriggeredLastConditionBlocks[triggered] = {};
}

// Move the earlier triggered block(s) down to where the last would have been
// inserted. This ensures that any values used by the triggered blocks are
// defined ahead of the uses.
triggered->moveBefore(builder.getInsertionBlock(),
builder.getInsertionPoint());
}

LogicalResult FIRRTLLowering::emitGuards(Location loc,
ArrayRef<Attribute> guards,
std::function<void(void)> emit) {
Expand Down Expand Up @@ -5456,7 +5505,7 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
return failure();

auto stderrOp = sim::StderrStreamOp::create(builder);
sim::TriggeredOp::create(builder, clock, cond, [&] {
addToSimTriggeredBlock(clock, cond, [&] {
sim::PrintFormattedProcOp::create(builder, *formatString, stderrOp);
});
return success();
Expand All @@ -5479,7 +5528,7 @@ LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {
if (failed(formatString))
return failure();

sim::TriggeredOp::create(builder, clock, cond, [&] {
addToSimTriggeredBlock(clock, cond, [&] {
auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
sim::PrintFormattedProcOp::create(builder, *formatString, fileOp);
});
Expand All @@ -5499,8 +5548,28 @@ LogicalResult FIRRTLLowering::visitStmt(FPrintFOp op) {

// FFlush lowers into $fflush statement.
LogicalResult FIRRTLLowering::visitStmt(FFlushOp op) {
if (circuitState.lowerToCore)
return op.emitOpError("lower-to-core does not support firrtl.fflush yet");
if (circuitState.lowerToCore) {
auto clock = getLoweredValue(op.getClock());
auto cond = getLoweredValue(op.getCond());
if (!clock || !cond)
return failure();

if (!op.getOutputFileAttr()) {
auto stderrOp = sim::StderrStreamOp::create(builder);
addToSimTriggeredBlock(clock, cond,
[&] { sim::FlushOp::create(builder, stderrOp); });
} else {
auto fileFormatString = lowerSimFormatString(
op.getOutputFileAttr(), op.getOutputFileSubstitutions());
if (failed(fileFormatString))
return failure();
addToSimTriggeredBlock(clock, cond, [&] {
auto fileOp = sim::GetFileOp::create(builder, *fileFormatString);
sim::FlushOp::create(builder, fileOp);
});
}
return success();
}

auto clock = getLoweredNonClockValue(op.getClock());
auto cond = getLoweredValue(op.getCond());
Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/Sim/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_circt_dialect_library(CIRCTSim
MLIRLLVMDialect
MLIRIR
MLIRPass
MLIRSCFDialect
MLIRTransforms
)

Expand Down
25 changes: 25 additions & 0 deletions lib/Dialect/Sim/SimOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "circt/Support/ProceduralRegionTrait.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Interfaces/FunctionImplementation.h"
#include "llvm/ADT/MapVector.h"
Expand Down Expand Up @@ -805,6 +806,30 @@ void TriggeredOp::build(OpBuilder &builder, OperationState &odsState,
bodyCtor();
}

LogicalResult TriggeredOp::canonicalize(TriggeredOp op,
PatternRewriter &rewriter) {
if (op.getCondition())
return failure();

Block *body = op.getBodyBlock();
if (!llvm::hasSingleElement(body->getOperations()))
return failure();

auto ifOp = dyn_cast<mlir::scf::IfOp>(body->front());
if (!ifOp || ifOp->getNumResults() != 0 || ifOp.elseBlock())
return failure();

op.getConditionMutable().append(ifOp.getCondition());

Block *thenBlock = ifOp.thenBlock();
auto insertionPoint = ifOp->getIterator();
body->getOperations().splice(insertionPoint, thenBlock->getOperations(),
thenBlock->begin(),
Block::iterator(thenBlock->getTerminator()));
rewriter.eraseOp(ifOp);
return success();
}

//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//
Expand Down
14 changes: 0 additions & 14 deletions test/Conversion/FIRRTLToHW/lower-to-core-errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,6 @@ firrtl.circuit "time_printf" {

// -----

firrtl.circuit "fflush_unsupported" {
firrtl.module @fflush_unsupported(
in %clock: !firrtl.clock,
in %enable: !firrtl.uint<1>) {
// expected-error @+2 {{'firrtl.fflush' op lower-to-core does not support firrtl.fflush yet}}
// expected-error @below {{'firrtl.fflush' op LowerToHW couldn't handle this operation}}
firrtl.fflush %clock, %enable, "out.txt"()
: !firrtl.clock, !firrtl.uint<1>
firrtl.skip
}
}

// -----

firrtl.circuit "force_unsupported" {
firrtl.module @force_unsupported(in %in: !firrtl.uint<42>) {
%foo = firrtl.verbatim.wire "foo" : () -> !firrtl.uint<42>
Expand Down
73 changes: 58 additions & 15 deletions test/Conversion/FIRRTLToHW/lower-to-core.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,49 @@ firrtl.circuit "LowerToCore" {
// CHECK: [[LIT1:%.+]] = sim.fmt.literal " @ "
// CHECK: [[HIER:%.+]] = sim.fmt.hier_path
// CHECK: [[NL:%.+]] = sim.fmt.literal "\0A"
// CHECK: [[MSG:%.+]] = sim.fmt.concat ([[LIT0]], [[FMTVAL]], [[LIT1]], [[HIER]], [[NL]])
// CHECK: [[STDERR:%.+]] = sim.stderr_stream
// CHECK: sim.triggered %clock if %enable {
// CHECK-NEXT: sim.proc.print [[MSG]] to [[STDERR]]
// CHECK-NEXT: }
// CHECK: [[MSG0:%.+]] = sim.fmt.concat ([[LIT0]], [[FMTVAL]], [[LIT1]], [[HIER]], [[NL]])
// CHECK: [[STDERR0:%.+]] = sim.stderr_stream
// CHECK-NOT: sv.assert
// CHECK-NOT: sv.fwrite
firrtl.printf %clock, %enable, "value=%d @ {{}}\0A"(%x, %hier)
: !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.fstring

// CHECK: [[FMTFILE1:%.+]] = sim.fmt.literal "out.txt"
// CHECK: [[MSG:%.+]] = sim.fmt.concat
// CHECK: sim.triggered %clock if %enable {
// CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]]
// CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE1]]
// CHECK-NEXT: }
// CHECK: [[MSG1:%.+]] = sim.fmt.concat
firrtl.fprintf %clock, %enable, "out.txt"(), "value=%d @ {{}}\0A"(%x, %hier)
: !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.fstring

// CHECK: [[LIT2:%.+]] = sim.fmt.literal "out"
// CHECK: [[FILEVAL:%.+]] = sim.fmt.dec %x signed : i4
// CHECK: [[LIT3:%.+]] = sim.fmt.literal ".txt"
// CHECK: [[FMTFILE2:%.+]] = sim.fmt.concat ([[LIT2]], [[FILEVAL]], [[LIT3]])
// CHECK: [[MSG:%.+]] = sim.fmt.concat
// CHECK: sim.triggered %clock if %enable {
// CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]]
// CHECK-NEXT: sim.proc.print [[MSG]] to [[FILE2]]
// CHECK-NEXT: }
// CHECK: [[MSG2:%.+]] = sim.fmt.concat
firrtl.fprintf %clock, %enable, "out%d.txt"(%x), "value=%d @ {{}}\0A"(%x, %hier)
: !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>, !firrtl.sint<4>, !firrtl.fstring

// CHECK: [[LIT4:%.+]] = sim.fmt.literal "out"
// CHECK: [[FILEVAL:%.+]] = sim.fmt.dec %x signed : i4
// CHECK: [[LIT5:%.+]] = sim.fmt.literal ".txt
// CHECK: [[FMTFILE3:%.+]] = sim.fmt.concat ([[LIT4]], [[FILEVAL]], [[LIT5]])
firrtl.fflush %clock, %enable, "out%d.txt"(%x)
: !firrtl.clock, !firrtl.uint<1>, !firrtl.sint<4>

// CHECK: [[STDERR1:%.+]] = sim.stderr_stream
// CHECK: sim.triggered %clock {
// CHECK-NEXT: scf.if %enable {
// CHECK-NEXT: sim.proc.print [[MSG0]] to [[STDERR0]]
// CHECK-NEXT: [[FILE1:%.+]] = sim.get_file [[FMTFILE1]]
// CHECK-NEXT: sim.proc.print [[MSG1]] to [[FILE1]]
// CHECK-NEXT: [[FILE2:%.+]] = sim.get_file [[FMTFILE2]]
// CHECK-NEXT: sim.proc.print [[MSG2]] to [[FILE2]]
// CHECK-NEXT: [[FILE3:%.+]] = sim.get_file [[FMTFILE3]]
// CHECK-NEXT: sim.flush [[FILE3]]
// CHECK-NEXT: sim.flush [[STDERR1]]
// CHECK-NEXT: }
// CHECK-NEXT: }
firrtl.fflush %clock, %enable
: !firrtl.clock, !firrtl.uint<1>

firrtl.skip
}

Expand All @@ -63,4 +75,35 @@ firrtl.circuit "LowerToCore" {
%sink = firrtl.instance sink @AnalogSink(in a: !firrtl.analog<8>)
firrtl.attach %a, %sink : !firrtl.analog<8>, !firrtl.analog<8>
}

// CHECK-LABEL: hw.module @InterleavedConditions(
firrtl.module @InterleavedConditions(
in %clock: !firrtl.clock,
in %enable_a: !firrtl.uint<1>,
in %enable_b: !firrtl.uint<1>) {
// CHECK: [[A0:%.+]] = sim.fmt.literal "a0\0A"
// CHECK: [[STDERR0:%.+]] = sim.stderr_stream
// CHECK: [[B0:%.+]] = sim.fmt.literal "b0\0A"
// CHECK: [[STDERR1:%.+]] = sim.stderr_stream
// CHECK: [[A1:%.+]] = sim.fmt.literal "a1\0A"
// CHECK: [[STDERR2:%.+]] = sim.stderr_stream
// CHECK: sim.triggered %clock {
// CHECK-NEXT: scf.if %enable_a {
// CHECK-NEXT: sim.proc.print [[A0]] to [[STDERR0]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %enable_b {
// CHECK-NEXT: sim.proc.print [[B0]] to [[STDERR1]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %enable_a {
// CHECK-NEXT: sim.proc.print [[A1]] to [[STDERR2]]
// CHECK-NEXT: }
// CHECK-NEXT: }
firrtl.printf %clock, %enable_a, "a0\0A"()
: !firrtl.clock, !firrtl.uint<1>
firrtl.printf %clock, %enable_b, "b0\0A"()
: !firrtl.clock, !firrtl.uint<1>
firrtl.printf %clock, %enable_a, "a1\0A"()
: !firrtl.clock, !firrtl.uint<1>
firrtl.skip
}
}
52 changes: 52 additions & 0 deletions test/Dialect/Sim/canonicalization.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,55 @@ func.func @string_get_special_chars() -> i8 {
// CHECK: return [[TMP]]
return %char : i8
}

//===----------------------------------------------------------------------===//
// TriggeredOp
//===----------------------------------------------------------------------===//

// CHECK-LABEL: hw.module @triggered_hoist_single_if(
hw.module @triggered_hoist_single_if(in %clock: !seq.clock, in %enable: i1) {
%msg = sim.fmt.literal "hello"
// CHECK: [[MSG:%.+]] = sim.fmt.literal "hello"
// CHECK: sim.triggered %clock if %enable {
// CHECK-NEXT: sim.proc.print [[MSG]]
// CHECK-NEXT: }
sim.triggered %clock {
scf.if %enable {
sim.proc.print %msg
}
}
hw.output
}

// CHECK-LABEL: hw.module @triggered_keep_interleaved_ifs(
hw.module @triggered_keep_interleaved_ifs(in %clock: !seq.clock, in %a: i1, in %b: i1) {
%a0 = sim.fmt.literal "a0"
%b0 = sim.fmt.literal "b0"
%a1 = sim.fmt.literal "a1"
// CHECK: [[A0:%.+]] = sim.fmt.literal "a0"
// CHECK: [[B0:%.+]] = sim.fmt.literal "b0"
// CHECK: [[A1:%.+]] = sim.fmt.literal "a1"
// CHECK: sim.triggered %clock {
// CHECK-NEXT: scf.if %a {
// CHECK-NEXT: sim.proc.print [[A0]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %b {
// CHECK-NEXT: sim.proc.print [[B0]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %a {
// CHECK-NEXT: sim.proc.print [[A1]]
// CHECK-NEXT: }
// CHECK-NEXT: }
sim.triggered %clock {
scf.if %a {
sim.proc.print %a0
}
scf.if %b {
sim.proc.print %b0
}
scf.if %a {
sim.proc.print %a1
}
}
hw.output
}
Loading
Loading