[Sim] Add printing operations and transformation from non-procedural to procedural flavor (#7292)

This commit is contained in:
fzi-hielscher 2024-07-17 14:09:59 +02:00 committed by GitHub
parent de80eb4381
commit 458717bc24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 722 additions and 1 deletions

View File

@ -12,3 +12,8 @@
add_circt_dialect(Sim sim)
add_circt_dialect_doc(Sim sim)
add_dependencies(circt-headers MLIRSimIncGen)
set(LLVM_TARGET_DEFINITIONS SimPasses.td)
mlir_tablegen(SimPasses.h.inc -gen-pass-decls)
add_public_tablegen_target(CIRCTSimTransformsIncGen)
add_dependencies(circt-headers CIRCTSimTransformsIncGen)

View File

@ -29,4 +29,24 @@
#define GET_OP_CLASSES
#include "circt/Dialect/Sim/Sim.h.inc"
namespace circt {
namespace sim {
/// Returns the value operand of a value formatting operation.
/// Returns a null value for all other operations.
static inline mlir::Value getFormattedValue(mlir::Operation *fmtOp) {
if (auto fmt = llvm::dyn_cast_or_null<circt::sim::FormatBinOp>(fmtOp))
return fmt.getValue();
if (auto fmt = llvm::dyn_cast_or_null<circt::sim::FormatDecOp>(fmtOp))
return fmt.getValue();
if (auto fmt = llvm::dyn_cast_or_null<circt::sim::FormatHexOp>(fmtOp))
return fmt.getValue();
if (auto fmt = llvm::dyn_cast_or_null<circt::sim::FormatCharOp>(fmtOp))
return fmt.getValue();
return {};
}
} // namespace sim
} // namespace circt
#endif // CIRCT_DIALECT_SIM_SIMOPS_H

View File

@ -327,4 +327,42 @@ def FormatStringConcatOp : SimOp<"fmt.concat", [Pure]> {
];
}
def PrintFormattedOp : SimOp<"print"> {
let summary = "Print a formatted string on a given clock and condition";
let description = [{
Evaluate a format string and print it to the simulation console on the
rising edge of the given clock, if, and only if, the condition argument
is 'true'.
Multiple print operations in the same module and on the same clock edge
are performed according to their order of occurence in the IR. The order
of printing for operations in different modules, instances or on different
clocks is undefined.
}];
let arguments = (ins FormatStringType:$input, ClockType:$clock, I1:$condition);
let hasCanonicalizeMethod = true;
let assemblyFormat = "$input `on` $clock `if` $condition attr-dict";
}
def PrintFormattedProcOp : SimOp<"proc.print"> {
let summary = "Print a formatted string within a procedural region";
let description = [{
Evaluate a format string and print it to the simulation console.
This operation must be within a procedural region.
}];
let arguments = (ins FormatStringType:$input);
let hasVerifier = true;
let hasCanonicalizeMethod = true;
let assemblyFormat = "$input attr-dict";
}
#endif // CIRCT_DIALECT_SIM_SIMOPS_TD

View File

@ -0,0 +1,31 @@
//===- SimPasses.h - Sim pass entry points ----------------------*- 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 file defines prototypes that expose pass constructors.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_DIALECT_SIM_SIMPASSES_H
#define CIRCT_DIALECT_SIM_SIMPASSES_H
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/StringRef.h"
namespace circt {
namespace sim {
#define GEN_PASS_DECL
#include "circt/Dialect/Sim/SimPasses.h.inc"
/// Generate the code for registering passes.
#define GEN_PASS_REGISTRATION
#include "circt/Dialect/Sim/SimPasses.h.inc"
} // namespace sim
} // namespace circt
#endif // CIRCT_DIALECT_SIM_SIMPASSES_H

View File

@ -0,0 +1,26 @@
//===-- SimPasses.td - Sim pass definition file ------------*- tablegen -*-===//
//
// 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 file contains definitions for passes that work on the Sim dialect.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_DIALECT_SIM_SEQPASSES
#define CIRCT_DIALECT_SIM_SEQPASSES
include "mlir/Pass/PassBase.td"
def ProceduralizeSim : Pass<"sim-proceduralize", "hw::HWModuleOp"> {
let summary = "Transform non-procedural to procedural operations.";
let description = [{Transform non-procedural simulation operations with clock
and enable to procedural operations wrapped in
a procedural region.}];
let dependentDialects = ["circt::hw::HWDialect, circt::seq::SeqDialect, mlir::scf::SCFDialect"];
}
#endif // CIRCT_DIALECT_SIM_SEQPASSES

View File

@ -34,6 +34,7 @@
#include "circt/Dialect/SSP/SSPPasses.h"
#include "circt/Dialect/SV/SVPasses.h"
#include "circt/Dialect/Seq/SeqPasses.h"
#include "circt/Dialect/Sim/SimPasses.h"
#include "circt/Dialect/SystemC/SystemCPasses.h"
#include "circt/Dialect/Verif/VerifPasses.h"
#include "circt/Tools/circt-bmc/Passes.h"
@ -72,6 +73,7 @@ inline void registerAllPasses() {
ibis::registerPasses();
hw::registerPasses();
pipeline::registerPasses();
sim::registerPasses();
ssp::registerPasses();
systemc::registerPasses();
verif::registerPasses();

View File

@ -33,3 +33,5 @@ add_circt_dialect_library(CIRCTSim
MLIRPass
MLIRTransforms
)
add_subdirectory(Transforms)

View File

@ -13,7 +13,6 @@
#include "circt/Dialect/Sim/SimOps.h"
#include "circt/Dialect/HW/ModuleImplementation.h"
#include "circt/Dialect/SV/SVOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Interfaces/FunctionImplementation.h"
@ -346,6 +345,53 @@ LogicalResult FormatStringConcatOp::canonicalize(FormatStringConcatOp op,
return success();
}
LogicalResult PrintFormattedOp::canonicalize(PrintFormattedOp op,
PatternRewriter &rewriter) {
// Remove ops with constant false condition.
if (auto cstCond = op.getCondition().getDefiningOp<hw::ConstantOp>()) {
if (cstCond.getValue().isZero()) {
rewriter.eraseOp(op);
return success();
}
}
return failure();
}
LogicalResult PrintFormattedProcOp::verify() {
// Check if we know for sure that the parent is not procedural.
auto *parentOp = getOperation()->getParentOp();
if (!parentOp)
return emitOpError("must be within a procedural region.");
if (isa<hw::HWDialect>(parentOp->getDialect())) {
if (!isa<hw::TriggeredOp>(parentOp))
return emitOpError("must be within a procedural region.");
return success();
}
if (isa<sv::SVDialect>(parentOp->getDialect())) {
if (!parentOp->hasTrait<sv::ProceduralRegion>())
return emitOpError("must be within a procedural region.");
return success();
}
// Don't fail for dialects that are not explicitly handled.
return success();
}
LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op,
PatternRewriter &rewriter) {
// Remove empty prints.
if (auto litInput = op.getInput().getDefiningOp<FormatLitOp>()) {
if (litInput.getLiteral().empty()) {
rewriter.eraseOp(op);
return success();
}
}
return failure();
}
//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,19 @@
add_circt_dialect_library(CIRCTSimTransforms
ProceduralizeSim.cpp
DEPENDS
CIRCTSimTransformsIncGen
LINK_LIBS PUBLIC
CIRCTHW
CIRCTSim
CIRCTSeq
CIRCTSV
CIRCTComb
CIRCTSupport
MLIRIR
MLIRPass
MLIRSCFDialect
MLIRTransformUtils
)

View File

@ -0,0 +1,267 @@
//===- ProceduralizeSim.cpp - Conversion to procedural operations ---------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Transform non-procedural simulation operations with clock and enable to
// procedural operations wrapped in a procedural region.
//
//===----------------------------------------------------------------------===//
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/Seq/SeqOps.h"
#include "circt/Dialect/Sim/SimOps.h"
#include "circt/Dialect/Sim/SimTypes.h"
#include "circt/Support/Debug.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Pass/Pass.h"
#define DEBUG_TYPE "proceduralize-sim"
namespace circt {
namespace sim {
#define GEN_PASS_DEF_PROCEDURALIZESIM
#include "circt/Dialect/Sim/SimPasses.h.inc"
} // namespace sim
} // namespace circt
using namespace llvm;
using namespace circt;
using namespace sim;
namespace {
struct ProceduralizeSimPass : impl::ProceduralizeSimBase<ProceduralizeSimPass> {
public:
void runOnOperation() override;
private:
LogicalResult proceduralizePrintOps(Value clock,
ArrayRef<PrintFormattedOp> printOps);
SmallVector<Operation *> getPrintTokens(PrintFormattedOp op);
void cleanup();
// Mapping Clock -> List of printf ops
SmallMapVector<Value, SmallVector<PrintFormattedOp>, 2> printfOpMap;
// List of formatting ops to be pruned after proceduralization.
SmallVector<Operation *> cleanupList;
};
} // namespace
LogicalResult ProceduralizeSimPass::proceduralizePrintOps(
Value clock, ArrayRef<PrintFormattedOp> printOps) {
// List of uniqued values to become arguments of the TriggeredOp.
SmallSetVector<Value, 4> arguments;
// Map printf ops -> flattened list of tokens
SmallDenseMap<PrintFormattedOp, SmallVector<Operation *>, 4> tokenMap;
SmallVector<Location> locs;
SmallDenseSet<Value, 1> alwaysEnabledConditions;
locs.reserve(printOps.size());
for (auto printOp : printOps) {
// Handle the print condition value. If it is not constant, it has to become
// a region argument. If it is constant false, skip the operation.
if (auto cstCond = printOp.getCondition().getDefiningOp<hw::ConstantOp>()) {
if (cstCond.getValue().isAllOnes())
alwaysEnabledConditions.insert(printOp.getCondition());
else
continue;
} else {
arguments.insert(printOp.getCondition());
}
// Accumulate locations
locs.push_back(printOp.getLoc());
// Get the flat list of formatting tokens and collect leaf tokens
SmallVector<Value> flatString;
if (auto concatInput =
printOp.getInput().getDefiningOp<FormatStringConcatOp>()) {
auto isAcyclic = concatInput.getFlattenedInputs(flatString);
if (failed(isAcyclic)) {
printOp.emitError("Cyclic format string cannot be proceduralized.");
return failure();
}
} else {
flatString.push_back(printOp.getInput());
}
auto &tokenList = tokenMap[printOp];
assert(tokenList.empty() && "printf operation visited twice.");
for (auto &token : flatString) {
auto *fmtOp = token.getDefiningOp();
if (!fmtOp) {
printOp.emitError("Proceduralization of format strings passed as block "
"argument is unsupported.");
return failure();
}
tokenList.push_back(fmtOp);
// For non-literal tokens, the value to be formatted has to become an
// argument.
if (!llvm::isa<FormatLitOp>(fmtOp)) {
auto fmtVal = getFormattedValue(fmtOp);
assert(!!fmtVal && "Unexpected foramtting token op.");
arguments.insert(fmtVal);
}
}
}
// Build the hw::TriggeredOp
OpBuilder builder(printOps.back());
auto fusedLoc = builder.getFusedLoc(locs);
SmallVector<Value> argVec = arguments.takeVector();
auto clockConv = builder.createOrFold<seq::FromClockOp>(fusedLoc, clock);
auto trigOp = builder.create<hw::TriggeredOp>(
fusedLoc,
hw::EventControlAttr::get(builder.getContext(),
hw::EventControl::AtPosEdge),
clockConv, argVec);
// Map the collected arguments to the newly created block arguments.
IRMapping argumentMapper;
unsigned idx = 0;
for (auto arg : argVec) {
argumentMapper.map(arg, trigOp.getBodyBlock()->getArgument(idx));
idx++;
}
// Materialize and map a 'true' constant within the TriggeredOp if required.
builder.setInsertionPointToStart(trigOp.getBodyBlock());
if (!alwaysEnabledConditions.empty()) {
auto cstTrue = builder.createOrFold<hw::ConstantOp>(
fusedLoc, IntegerAttr::get(builder.getI1Type(), 1));
for (auto cstCond : alwaysEnabledConditions)
argumentMapper.map(cstCond, cstTrue);
}
SmallDenseMap<Operation *, Operation *> cloneMap;
Value prevConditionValue;
Block *prevConditionBlock;
for (auto printOp : printOps) {
// Throw away disabled prints
if (auto cstCond = printOp.getCondition().getDefiningOp<hw::ConstantOp>()) {
if (cstCond.getValue().isZero()) {
printOp.erase();
continue;
}
}
// Create a copy of the required token operations within the TriggeredOp's
// body.
auto tokens = tokenMap[printOp];
SmallVector<Value> clonedOperands;
builder.setInsertionPointToStart(trigOp.getBodyBlock());
for (auto *token : tokens) {
auto &fmtCloned = cloneMap[token];
if (!fmtCloned)
fmtCloned = builder.clone(*token, argumentMapper);
clonedOperands.push_back(fmtCloned->getResult(0));
}
// Concatenate tokens to a single value if necessary.
Value procPrintInput;
if (clonedOperands.size() != 1)
procPrintInput = builder.createOrFold<FormatStringConcatOp>(
printOp.getLoc(), clonedOperands);
else
procPrintInput = clonedOperands.front();
// Check if we can reuse the previous conditional block.
auto condArg = argumentMapper.lookup(printOp.getCondition());
if (condArg != prevConditionValue)
prevConditionBlock = nullptr;
auto *condBlock = prevConditionBlock;
// If not, create a new scf::IfOp for the condition.
if (!condBlock) {
builder.setInsertionPointToEnd(trigOp.getBodyBlock());
auto ifOp = builder.create<mlir::scf::IfOp>(printOp.getLoc(), TypeRange{},
condArg, true, false);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
builder.create<mlir::scf::YieldOp>(printOp.getLoc());
condBlock = builder.getBlock();
prevConditionValue = condArg;
prevConditionBlock = condBlock;
}
// Create the procedural print operation and prune the operations outside of
// the TriggeredOp.
builder.setInsertionPoint(condBlock->getTerminator());
builder.create<PrintFormattedProcOp>(printOp.getLoc(), procPrintInput);
cleanupList.push_back(printOp.getInput().getDefiningOp());
printOp.erase();
}
return success();
}
// Prune the DAGs of formatting tokens left outside of the newly created
// TriggeredOps.
void ProceduralizeSimPass::cleanup() {
SmallVector<Operation *> cleanupNextList;
SmallDenseSet<Operation *> erasedOps;
bool noChange = true;
while (!cleanupList.empty() || !cleanupNextList.empty()) {
if (cleanupList.empty()) {
if (noChange)
break;
cleanupList = std::move(cleanupNextList);
cleanupNextList = {};
noChange = true;
}
auto *opToErase = cleanupList.pop_back_val();
if (erasedOps.contains(opToErase))
continue;
if (opToErase->getUses().empty()) {
// Remove a dead op. If it is a concat remove its operands, too.
if (auto concat = dyn_cast<FormatStringConcatOp>(opToErase))
for (auto operand : concat.getInputs())
cleanupNextList.push_back(operand.getDefiningOp());
opToErase->erase();
erasedOps.insert(opToErase);
noChange = false;
} else {
// Op still has uses, revisit later.
cleanupNextList.push_back(opToErase);
}
}
}
void ProceduralizeSimPass::runOnOperation() {
LLVM_DEBUG(debugPassHeader(this) << "\n");
printfOpMap.clear();
cleanupList.clear();
auto theModule = getOperation();
// Collect printf operations grouped by their clock.
theModule.walk<mlir::WalkOrder::PreOrder>(
[&](PrintFormattedOp op) { printfOpMap[op.getClock()].push_back(op); });
// Create a hw::TriggeredOp for each clock
for (auto &[clock, printOps] : printfOpMap)
if (failed(proceduralizePrintOps(clock, printOps))) {
signalPassFailure();
return;
}
cleanup();
}

View File

@ -0,0 +1,18 @@
// RUN: circt-opt --canonicalize %s | FileCheck %s
// CHECK-LABEL: hw.module @always_disabled
// CHECK-NOT: sim.print
hw.module @always_disabled(in %clock: !seq.clock) {
%false = hw.constant false
%lit = sim.fmt.lit "Foo"
sim.print %lit on %clock if %false
}
// CHECK-LABEL: hw.module @emtpy_proc_print
// CHECK-NOT: sim.proc.print
hw.module @emtpy_proc_print(in %trigger: i1) {
hw.triggered posedge %trigger {
%epsilon = sim.fmt.lit ""
sim.proc.print %epsilon
}
}

View File

@ -0,0 +1,9 @@
// RUN: circt-opt --sim-proceduralize --split-input-file --verify-diagnostics %s
hw.module @cyclic_concat(in %clk : !seq.clock) {
%true = hw.constant true
%ping = sim.fmt.concat (%pong)
%pong = sim.fmt.concat (%ping)
// expected-error @below {{Cyclic format string cannot be proceduralized.}}
sim.print %ping on %clk if %true
}

View File

@ -0,0 +1,218 @@
// RUN: circt-opt --sim-proceduralize --canonicalize %s | FileCheck %s
// CHECK-LABEL: @basic_print1
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %[[TRG]] {
// CHECK-NEXT: %[[LIT:.*]] = sim.fmt.lit "Test"
// CHECK-NEXT: sim.proc.print %[[LIT]]
// CHECK-NEXT: }
hw.module @basic_print1(in %clk : !seq.clock) {
%true = hw.constant true
%test = sim.fmt.lit "Test"
sim.print %test on %clk if %true
}
// CHECK-LABEL: @basic_print2
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %[[TRG]](%cond) : i1 {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: i1):
// CHECK-DAG: %[[LIT1:.*]] = sim.fmt.lit "Not with a bang but a \00"
// CHECK-DAG: %[[LIT0:.*]] = sim.fmt.lit "This is the way the world ends\0A"
// CHECK: scf.if %[[ARG]] {
// CHECK-NEXT: sim.proc.print %[[LIT0]]
// CHECK-NEXT: sim.proc.print %[[LIT0]]
// CHECK-NEXT: sim.proc.print %[[LIT0]]
// CHECK-NEXT: sim.proc.print %[[LIT1]]
// CHECK-NEXT: }
// CHECK-NEXT: }
hw.module @basic_print2(in %clk : !seq.clock, in %cond : i1) {
%0 = sim.fmt.lit "Not with a bang but a \00"
%1 = sim.fmt.lit "This is the way the world ends\0A"
sim.print %1 on %clk if %cond
sim.print %1 on %clk if %cond
sim.print %1 on %clk if %cond
sim.print %0 on %clk if %cond
}
// CHECK-LABEL: @basic_print3
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %[[TRG]](%val) : i32 {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: i32):
// CHECK-DAG: %[[LB:.*]] = sim.fmt.lit "Bin: "
// CHECK-DAG: %[[LD:.*]] = sim.fmt.lit ", Dec: "
// CHECK-DAG: %[[LH:.*]] = sim.fmt.lit ", Hex: "
// CHECK-DAG: %[[FB:.*]] = sim.fmt.bin %[[ARG]] : i32
// CHECK-DAG: %[[FD:.*]] = sim.fmt.dec %[[ARG]] : i32
// CHECK-DAG: %[[FH:.*]] = sim.fmt.hex %[[ARG]] : i32
// CHECK-DAG: %[[CAT:.*]] = sim.fmt.concat (%[[LB]], %[[FB]], %[[LD]], %[[FD]], %[[LH]], %[[FH]])
// CHECK: sim.proc.print %[[CAT]]
// CHECK-NEXT: }
hw.module @basic_print3(in %clk : !seq.clock, in %val: i32) {
%true = hw.constant true
%comma = sim.fmt.lit ", "
%bin_lit = sim.fmt.lit "Bin: "
%bin_val = sim.fmt.bin %val : i32
%bin_cat = sim.fmt.concat (%bin_lit, %bin_val)
%dec_lit = sim.fmt.lit "Dec: "
%dec_val = sim.fmt.dec %val : i32
%dec_cat = sim.fmt.concat (%dec_lit, %dec_val)
%hex_lit = sim.fmt.lit "Hex: "
%hex_val = sim.fmt.hex %val : i32
%hex_cat = sim.fmt.concat (%hex_lit, %hex_val)
%str = sim.fmt.concat (%bin_cat, %comma, %dec_cat, %comma, %hex_cat)
sim.print %str on %clk if %true
}
// CHECK-LABEL: @multi_args
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %0(%a, %b, %c) : i8, i8, i8 {
// CHECK-NEXT: ^bb0(%[[ARG0:.*]]: i8, %[[ARG1:.*]]: i8, %[[ARG2:.*]]: i8):
// CHECK-DAG: %[[COM:.*]] = sim.fmt.lit ", "
// CHECK-DAG: %[[B0:.*]] = sim.fmt.bin %[[ARG0]] : i8
// CHECK-DAG: %[[H0:.*]] = sim.fmt.hex %[[ARG0]] : i8
// CHECK-DAG: %[[B1:.*]] = sim.fmt.bin %[[ARG1]] : i8
// CHECK-DAG: %[[H1:.*]] = sim.fmt.hex %[[ARG1]] : i8
// CHECK-DAG: %[[B2:.*]] = sim.fmt.bin %[[ARG2]] : i8
// CHECK-DAG: %[[H2:.*]] = sim.fmt.hex %[[ARG2]] : i8
// CHECK-DAG: %[[CAT:.*]] = sim.fmt.concat (%[[B0]], %[[B1]], %[[B2]], %[[COM]], %[[H0]], %[[H1]], %[[H2]])
// CHECK: sim.proc.print %[[CAT]]
// CHECK-NEXT: }
hw.module @multi_args(in %clk : !seq.clock, in %a: i8, in %b: i8, in %c: i8) {
%true = hw.constant true
%comma = sim.fmt.lit ", "
%bina = sim.fmt.bin %a : i8
%binb = sim.fmt.bin %b : i8
%binc = sim.fmt.bin %c : i8
%hexa = sim.fmt.hex %a : i8
%hexb = sim.fmt.hex %b : i8
%hexc = sim.fmt.hex %c : i8
%cat = sim.fmt.concat (%bina, %binb, %binc, %comma, %hexa, %hexb, %hexc)
sim.print %cat on %clk if %true
}
// CHECK-LABEL: @multi_clock
// CHECK-NEXT: %[[TRGA:.*]] = seq.from_clock %clka
// CHECK-NEXT: hw.triggered posedge %[[TRGA]](%val) : i32 {
// CHECK-NEXT: ^bb0(%[[ARGA:.*]]: i32):
// CHECK-DAG: %[[LA0:.*]] = sim.fmt.lit "Val is 0x"
// CHECK-DAG: %[[LA1:.*]] = sim.fmt.lit " on A."
// CHECK-DAG: %[[FA:.*]] = sim.fmt.hex %[[ARGA]] : i32
// CHECK-DAG: %[[CATA:.*]] = sim.fmt.concat (%[[LA0]], %[[FA]], %[[LA1]])
// CHECK: sim.proc.print %[[CATA]]
// CHECK-NEXT: }
// CHECK-NEXT: %[[TRGB:.*]] = seq.from_clock %clkb
// CHECK-NEXT: hw.triggered posedge %[[TRGB]](%val) : i32 {
// CHECK-NEXT: ^bb0(%[[ARGB:.*]]: i32):
// CHECK-DAG: %[[LB0:.*]] = sim.fmt.lit "Val is 0x"
// CHECK-DAG: %[[LB1:.*]] = sim.fmt.lit " on B."
// CHECK-DAG: %[[FB:.*]] = sim.fmt.hex %[[ARGB]] : i32
// CHECK-DAG: %[[CATB:.*]] = sim.fmt.concat (%[[LB0]], %[[FB]], %[[LB1]])
// CHECK: sim.proc.print %[[CATB]]
// CHECK-NEXT: }
// CHECK-NEXT: %[[TRGC:.*]] = seq.from_clock %clkc
// CHECK-NEXT: hw.triggered posedge %[[TRGC]](%val) : i32 {
// CHECK-NEXT: ^bb0(%[[ARGC:.*]]: i32):
// CHECK-DAG: %[[LC0:.*]] = sim.fmt.lit "Val is 0x"
// CHECK-DAG: %[[LC1:.*]] = sim.fmt.lit " on C."
// CHECK-DAG: %[[FC:.*]] = sim.fmt.hex %[[ARGC]] : i32
// CHECK-DAG: %[[CATC:.*]] = sim.fmt.concat (%[[LC0]], %[[FC]], %[[LC1]])
// CHECK: sim.proc.print %[[CATC]]
// CHECK-NEXT: }
hw.module @multi_clock(in %clka : !seq.clock, in %clkb : !seq.clock, in %clkc : !seq.clock, in %val: i32) {
%true = hw.constant true
%pre = sim.fmt.lit "Val is 0x"
%hex_val = sim.fmt.hex %val : i32
%onA = sim.fmt.lit " on A."
%onB = sim.fmt.lit " on B."
%onC = sim.fmt.lit " on C."
%catA = sim.fmt.concat (%pre, %hex_val, %onA)
sim.print %catA on %clka if %true
%catB = sim.fmt.concat (%pre, %hex_val, %onB)
sim.print %catB on %clkb if %true
%catC = sim.fmt.concat (%pre, %hex_val, %onC)
sim.print %catC on %clkc if %true
}
// CHECK-LABEL: @sequence
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %[[TRG]](%conda, %condb, %val) : i1, i1, i32 {
// CHECK-NEXT: ^bb0(%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i1, %[[ARG2:.*]]: i32):
// CHECK-DAG: %[[L1:.*]] = sim.fmt.lit "#1"
// CHECK-DAG: %[[L2:.*]] = sim.fmt.lit "#2"
// CHECK-DAG: %[[L3:.*]] = sim.fmt.lit "#3"
// CHECK-DAG: %[[L4:.*]] = sim.fmt.lit "#4"
// CHECK-DAG: %[[L5:.*]] = sim.fmt.lit "#5"
// CHECK-DAG: %[[L6:.*]] = sim.fmt.lit "#6"
// CHECK-DAG: %[[BIN:.*]] = sim.fmt.bin %[[ARG2]] : i32
// CHECK: scf.if %[[ARG0]] {
// CHECK-NEXT: sim.proc.print %[[L1]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %[[ARG1]] {
// CHECK-NEXT: sim.proc.print %[[L2]]
// CHECK-NEXT: sim.proc.print %[[L3]]
// CHECK-NEXT: sim.proc.print %[[L4]]
// CHECK-NEXT: }
// CHECK-NEXT: scf.if %[[ARG0]] {
// CHECK-NEXT: sim.proc.print %[[L5]]
// CHECK-NEXT: }
// CHECK-NEXT: sim.proc.print %[[BIN]]
// CHECK-NEXT: scf.if %[[ARG0]] {
// CHECK-NEXT: sim.proc.print %[[L6]]
// CHECK-NEXT: }
// CHECK-NEXT: }
hw.module @sequence(in %clk: !seq.clock, in %conda: i1, in %condb: i1, in %val : i32) {
%true = hw.constant true
%false = hw.constant false
%1 = sim.fmt.lit "#1"
sim.print %1 on %clk if %conda
%2 = sim.fmt.lit "#2"
sim.print %2 on %clk if %condb
%3 = sim.fmt.lit "#3"
sim.print %3 on %clk if %condb
%cdis = sim.fmt.lit "--"
sim.print %cdis on %clk if %false
%4 = sim.fmt.lit "#4"
sim.print %4 on %clk if %condb
%5 = sim.fmt.lit "#5"
sim.print %5 on %clk if %conda
%cen = sim.fmt.bin %val : i32
sim.print %cen on %clk if %true
%6 = sim.fmt.lit "#6"
sim.print %6 on %clk if %conda
}
// CHECK-LABEL: @condition_as_val
// CHECK-NEXT: %[[TRG:.*]] = seq.from_clock %clk
// CHECK-NEXT: hw.triggered posedge %[[TRG]](%condval) : i1 {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: i1):
// CHECK-NEXT: %[[BIN:.*]] = sim.fmt.bin %[[ARG]] : i1
// CHECK-NEXT: scf.if %[[ARG]] {
// CHECK-NEXT: sim.proc.print %[[BIN]]
// CHECK-NEXT: }
// CHECK-NEXT: }
hw.module @condition_as_val(in %clk: !seq.clock, in %condval: i1) {
%bin = sim.fmt.bin %condval : i1
sim.print %bin on %clk if %condval
}

View File

@ -23,3 +23,22 @@ hw.module @fmt_infinite_concat_canonicalize(in %val : i8, out res: !sim.fstring)
%cat = sim.fmt.concat (%4, %c, %5)
hw.output %cat : !sim.fstring
}
// -----
hw.module @proc_print_hw() {
%lit = sim.fmt.lit "Nope"
// expected-error @below {{must be within a procedural region.}}
sim.proc.print %lit
}
// -----
sv.macro.decl @SOMEMACRO
hw.module @proc_print_sv() {
%lit = sim.fmt.lit "Nope"
sv.ifdef @SOMEMACRO {
// expected-error @below {{must be within a procedural region.}}
sim.proc.print %lit
}
}

View File

@ -77,6 +77,7 @@ target_link_libraries(circt-opt
CIRCTSeqToSV
CIRCTSeqTransforms
CIRCTSimToSV
CIRCTSimTransforms
CIRCTSSP
CIRCTSSPTransforms
CIRCTCFToHandshake