diff --git a/include/circt/Dialect/Seq/SeqOps.h b/include/circt/Dialect/Seq/SeqOps.h index ac09831be4bb..0bbbdbce0ce5 100644 --- a/include/circt/Dialect/Seq/SeqOps.h +++ b/include/circt/Dialect/Seq/SeqOps.h @@ -70,6 +70,12 @@ createConstantInitialValue(OpBuilder builder, Operation *constantLike); // initial op. Value unwrapImmutableValue(mlir::TypedValue immutableVal); +// Helper function to merge initial ops within the block into a single initial +// op. Return failure if we cannot topologically sort the initial ops. +// Return null if there is no initial op in the block. Return the initial op +// otherwise. +FailureOr mergeInitialOps(Block *block); + } // namespace seq } // namespace circt diff --git a/include/circt/Dialect/Seq/SeqOps.td b/include/circt/Dialect/Seq/SeqOps.td index 7f05ffdb84df..4126f75b0fe5 100644 --- a/include/circt/Dialect/Seq/SeqOps.td +++ b/include/circt/Dialect/Seq/SeqOps.td @@ -711,7 +711,7 @@ def InitialOp : SeqOp<"initial", [SingleBlock, See the Seq dialect rationale for a longer description. }]; - let arguments = (ins); + let arguments = (ins Variadic: $inputs); let results = (outs Variadic); // seq.immutable values let regions = (region SizedRegion<1>:$body); let hasVerifier = 1; @@ -721,7 +721,7 @@ def InitialOp : SeqOp<"initial", [SingleBlock, ]; let assemblyFormat = [{ - $body attr-dict `:` type(results) + `(` $inputs `)` $body attr-dict `:` functional-type($inputs, results) }]; let extraClassDeclaration = [{ @@ -740,3 +740,28 @@ def YieldOp : SeqOp<"yield", let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } + +def FromImmutableOp : SeqOp<"from_immutable", [Pure]> { + let summary = "Cast from an immutable type to a wire type"; + + let arguments = (ins ImmutableType:$input); + let results = (outs AnyType:$output); + + let assemblyFormat = "$input attr-dict `:` functional-type(operands, results)"; +} + +def GetInitialValueOp : SeqOp<"get_initial_value", [Pure]> { + let summary = "Get an initial value of the input"; + let description = [{ + This operation freezes a HW value while the initialization phase and + returns the frozen value as `seq.immutable` type. The input value must be valid + at the initialization and immutable through the initialization phase and otherwise + the result value is undefined. In other words time-variant values such as registers + cannot be used as an input. + }]; + + let arguments = (ins AnyType:$input); + let results = (outs ImmutableType:$output); + + let assemblyFormat = "$input attr-dict `:` functional-type(operands, results)"; +} diff --git a/integration_test/Bindings/Python/dialects/seq.py b/integration_test/Bindings/Python/dialects/seq.py index 27cb8162a999..e928aba589ea 100644 --- a/integration_test/Bindings/Python/dialects/seq.py +++ b/integration_test/Bindings/Python/dialects/seq.py @@ -27,10 +27,10 @@ def top(module): poweron_value = hw.ConstantOp.create(i32, 42).result # CHECK: %[[INPUT_VAL:.+]] = hw.constant 45 reg_input = hw.ConstantOp.create(i32, 45).result - # CHECK-NEXT: %[[POWERON_VAL:.+]] = seq.initial { + # CHECK-NEXT: %[[POWERON_VAL:.+]] = seq.initial() { # CHECK-NEXT: %[[C42:.+]] = hw.constant 42 : i32 # CHECK-NEXT: seq.yield %[[C42]] : i32 - # CHECK-NEXT: } : !seq.immutable + # CHECK-NEXT: } : () -> !seq.immutable # CHECK: %[[DATA_VAL:.+]] = seq.compreg %[[INPUT_VAL]], %clk reset %rst, %[[RESET_VAL]] initial %[[POWERON_VAL]] reg = seq.CompRegOp(i32, reg_input, diff --git a/integration_test/arcilator/JIT/reg.mlir b/integration_test/arcilator/JIT/reg.mlir index c985c2de04a4..ea610276ae0f 100644 --- a/integration_test/arcilator/JIT/reg.mlir +++ b/integration_test/arcilator/JIT/reg.mlir @@ -19,12 +19,12 @@ hw.module @counter(in %clk: i1, out o1: i8, out o2: i8) { %r0 = seq.compreg %added1, %seq_clk initial %0#0 : i8 %r1 = seq.compreg %added2, %seq_clk initial %0#1 : i8 - %0:2 = seq.initial { + %0:2 = seq.initial () { %1 = func.call @random() : () -> i32 %2 = comb.extract %1 from 0 : (i32) -> i8 %3 = hw.constant 5 : i8 seq.yield %2, %3: i8, i8 - } : !seq.immutable, !seq.immutable + } : () -> (!seq.immutable, !seq.immutable) %one = hw.constant 1 : i8 %added1 = comb.add %r0, %one : i8 diff --git a/lib/Bindings/Python/dialects/seq.py b/lib/Bindings/Python/dialects/seq.py index 548418702b4b..2f43ccbb3de7 100644 --- a/lib/Bindings/Python/dialects/seq.py +++ b/lib/Bindings/Python/dialects/seq.py @@ -99,7 +99,7 @@ def __init__(self, if power_on_value.owner is None: assert False, "Initial value must not be port" elif isinstance(power_on_value.owner.opview, hw.ConstantOp): - init = InitialOp([seq.ImmutableType.get(power_on_value.type)]) + init = InitialOp([seq.ImmutableType.get(power_on_value.type)], []) init.body.blocks.append() with InsertionPoint(init.body.blocks[0]): cloned_constant = power_on_value.owner.clone() diff --git a/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp b/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp index 46d5aed443d5..4eeffe054388 100644 --- a/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp +++ b/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp @@ -35,12 +35,11 @@ static LogicalResult convertInitialValue(seq::CompRegOp reg, if (!reg.getInitialValue()) return values.push_back({}), success(); - // unrealized_conversion_cast to normal type + // Use from_immutable cast to convert the seq.immutable type to the reg's + // type. OpBuilder builder(reg); - auto init = builder - .create( - reg.getLoc(), reg.getType(), reg.getInitialValue()) - .getResult(0); + auto init = builder.create(reg.getLoc(), reg.getType(), + reg.getInitialValue()); values.push_back(init); return success(); diff --git a/lib/Conversion/SeqToSV/SeqToSV.cpp b/lib/Conversion/SeqToSV/SeqToSV.cpp index dd3931dc6eb5..7874774f2156 100644 --- a/lib/Conversion/SeqToSV/SeqToSV.cpp +++ b/lib/Conversion/SeqToSV/SeqToSV.cpp @@ -23,6 +23,7 @@ #include "circt/Dialect/SV/SVOps.h" #include "circt/Dialect/Seq/SeqOps.h" #include "circt/Support/Naming.h" +#include "mlir/Analysis/TopologicalSortUtils.h" #include "mlir/IR/Builders.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/ImplicitLocOpBuilder.h" @@ -59,11 +60,10 @@ struct SeqToSVPass : public impl::LowerSeqToSVBase { namespace { struct ModuleLoweringState { ModuleLoweringState(HWModuleOp module) - : initalOpLowering(module), module(module) {} + : immutableValueLowering(module), module(module) {} - struct InitialOpLowering { - InitialOpLowering(hw::HWModuleOp module) - : builder(module.getModuleBody()), module(module) {} + struct ImmutableValueLowering { + ImmutableValueLowering(hw::HWModuleOp module) : module(module) {} // Lower initial ops. LogicalResult lower(); @@ -82,9 +82,8 @@ struct ModuleLoweringState { // defined in SV initial op. MapVector, Value> mapping; - OpBuilder builder; hw::HWModuleOp module; - } initalOpLowering; + } immutableValueLowering; struct FragmentInfo { bool needsRegFragment = false; @@ -94,21 +93,39 @@ struct ModuleLoweringState { HWModuleOp module; }; -LogicalResult ModuleLoweringState::InitialOpLowering::lower() { - auto loweringFailed = module - .walk([&](seq::InitialOp initialOp) { - if (failed(lower(initialOp))) - return mlir::WalkResult::interrupt(); - return mlir::WalkResult::advance(); - }) - .wasInterrupted(); - return LogicalResult::failure(loweringFailed); +LogicalResult ModuleLoweringState::ImmutableValueLowering::lower() { + auto result = mergeInitialOps(module.getBodyBlock()); + if (failed(result)) + return failure(); + + auto initialOp = *result; + if (!initialOp) + return success(); + + return lower(initialOp); } LogicalResult -ModuleLoweringState::InitialOpLowering::lower(seq::InitialOp initialOp) { +ModuleLoweringState::ImmutableValueLowering::lower(seq::InitialOp initialOp) { + OpBuilder builder = OpBuilder::atBlockBegin(module.getBodyBlock()); if (!svInitialOp) svInitialOp = builder.create(initialOp->getLoc()); + // Replace immutable operands passed to initial op with already lowered + // values. + for (auto [blockArgument, operand] : + llvm::zip(initialOp.getBodyBlock()->getArguments(), + initialOp->getOpOperands())) { + + auto immut = operand.get().getDefiningOp(); + if (!immut) + return initialOp.emitError() + << "invalid operand to initial op: " << operand.get(); + blockArgument.replaceAllUsesWith(immut.getInput()); + operand.drop(); + if (immut.use_empty()) + immut.erase(); + } + auto loc = initialOp.getLoc(); llvm::SmallVector results; @@ -127,10 +144,10 @@ ModuleLoweringState::InitialOpLowering::lower(seq::InitialOp initialOp) { } svInitialOp.getBodyBlock()->getOperations().splice( - svInitialOp.begin(), initialOp.getBodyBlock()->getOperations()); + svInitialOp.end(), initialOp.getBodyBlock()->getOperations()); assert(initialOp->use_empty()); - initialOp->erase(); + initialOp.erase(); yieldOp->erase(); return success(); } @@ -200,7 +217,7 @@ class CompRegLower : public OpConversionPattern { auto module = reg->template getParentOfType(); const auto &initial = moduleLoweringStates.find(module.getModuleNameAttr()) - ->second.initalOpLowering; + ->second.immutableValueLowering; Value initialValue = initial.lookupImmutableValue(init); @@ -247,6 +264,49 @@ void CompRegLower::createAssign( }); } +/// Lower FromImmutable to `sv.reg` and `sv.initial`. +class FromImmutableLowering : public OpConversionPattern { +public: + FromImmutableLowering( + TypeConverter &typeConverter, MLIRContext *context, + const MapVector &moduleLoweringStates) + : OpConversionPattern(typeConverter, context), + moduleLoweringStates(moduleLoweringStates) {} + + using OpAdaptor = typename OpConversionPattern::OpAdaptor; + + LogicalResult + matchAndRewrite(FromImmutableOp fromImmutableOp, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const final { + Location loc = fromImmutableOp.getLoc(); + + auto regTy = ConversionPattern::getTypeConverter()->convertType( + fromImmutableOp.getType()); + auto svReg = rewriter.create(loc, regTy); + + auto regVal = rewriter.create(loc, svReg); + + // Lower initial values. + auto module = fromImmutableOp->template getParentOfType(); + const auto &initial = moduleLoweringStates.find(module.getModuleNameAttr()) + ->second.immutableValueLowering; + + Value initialValue = + initial.lookupImmutableValue(fromImmutableOp.getInput()); + + OpBuilder::InsertionGuard guard(rewriter); + auto in = initial.getSVInitial(); + rewriter.setInsertionPointToEnd(in.getBodyBlock()); + rewriter.create(fromImmutableOp->getLoc(), svReg, + initialValue); + + rewriter.replaceOp(fromImmutableOp, regVal); + return success(); + } + +private: + const MapVector &moduleLoweringStates; +}; // Lower seq.clock_gate to a fairly standard clock gate implementation. // class ClockGateLowering : public OpConversionPattern { @@ -537,7 +597,7 @@ void SeqToSVPass::runOnOperation() { moduleLoweringStates.try_emplace(module.getModuleNameAttr(), ModuleLoweringState(module)); - mlir::parallelForEach( + auto result = mlir::failableParallelForEach( &getContext(), moduleLoweringStates, [&](auto &moduleAndState) { auto &state = moduleAndState.second; auto module = state.module; @@ -561,9 +621,12 @@ void SeqToSVPass::runOnOperation() { } needsMemRandomization = true; } - (void)state.initalOpLowering.lower(); + return state.immutableValueLowering.lower(); }); + if (failed(result)) + return signalPassFailure(); + auto randomInitFragmentName = FlatSymbolRefAttr::get(context, "RANDOM_INIT_FRAGMENT"); auto randomInitRegFragmentName = @@ -605,6 +668,8 @@ void SeqToSVPass::runOnOperation() { moduleLoweringStates); patterns.add>( typeConverter, context, lowerToAlwaysFF, moduleLoweringStates); + patterns.add(typeConverter, context, + moduleLoweringStates); patterns.add>(typeConverter, context); patterns.add>(typeConverter, context); patterns.add(typeConverter, context); diff --git a/lib/Dialect/Arc/Transforms/LowerState.cpp b/lib/Dialect/Arc/Transforms/LowerState.cpp index d901b0170e23..f98a1807ed97 100644 --- a/lib/Dialect/Arc/Transforms/LowerState.cpp +++ b/lib/Dialect/Arc/Transforms/LowerState.cpp @@ -190,6 +190,10 @@ Value ClockLowering::materializeValue(Value value) { return {}; if (auto mapped = materializedValues.lookupOrNull(value)) return mapped; + if (auto fromImmutable = value.getDefiningOp()) + // Immutable value is pre-materialized so directly lookup the input. + return materializedValues.lookup(fromImmutable.getInput()); + if (!shouldMaterialize(value)) return value; @@ -427,19 +431,27 @@ LogicalResult ModuleLowering::lowerPrimaryOutputs() { } LogicalResult ModuleLowering::lowerInitials() { - // Move all operations except for seq.yield to arc.initial op. - for (auto op : moduleOp.getOps()) { - auto terminator = cast(op.getBodyBlock()->getTerminator()); - getInitial().builder.getBlock()->getOperations().splice( - getInitial().builder.getBlock()->begin(), - op.getBodyBlock()->getOperations()); - - // Map seq.initial results to operands of the seq.yield op. - for (auto [result, operand] : - llvm::zip(op.getResults(), terminator.getOperands())) - getInitial().materializedValues.map(result, operand); - terminator.erase(); - } + // Merge all seq.initial ops into a single seq.initial op. + auto result = circt::seq::mergeInitialOps(moduleOp.getBodyBlock()); + if (failed(result)) + return moduleOp.emitError() << "initial ops cannot be topologically sorted"; + + auto initialOp = *result; + if (!initialOp) // There is no seq.initial op. + return success(); + + // Move the operations of the merged initial op into the builder's block. + auto terminator = + cast(initialOp.getBodyBlock()->getTerminator()); + getInitial().builder.getBlock()->getOperations().splice( + getInitial().builder.getBlock()->begin(), + initialOp.getBodyBlock()->getOperations()); + + // Map seq.initial results to their corresponding operands. + for (auto [result, operand] : + llvm::zip(initialOp.getResults(), terminator.getOperands())) + getInitial().materializedValues.map(result, operand); + terminator.erase(); return success(); } diff --git a/lib/Dialect/Seq/SeqOps.cpp b/lib/Dialect/Seq/SeqOps.cpp index be93665a9f35..faa3efbe829a 100644 --- a/lib/Dialect/Seq/SeqOps.cpp +++ b/lib/Dialect/Seq/SeqOps.cpp @@ -15,6 +15,7 @@ #include "circt/Dialect/Sim/SimTypes.h" #include "circt/Support/CustomDirectiveImpl.h" #include "circt/Support/FoldUtils.h" +#include "mlir/Analysis/TopologicalSortUtils.h" #include "mlir/IR/Builders.h" #include "mlir/IR/DialectImplementation.h" #include "mlir/IR/Matchers.h" @@ -1020,6 +1021,7 @@ FirMemory::FirMemory(hw::HWModuleGeneratedOp op) { } LogicalResult InitialOp::verify() { + // Check outputs. auto *terminator = this->getBody().front().getTerminator(); if (terminator->getOperands().size() != getNumResults()) return emitError() << "result type doesn't match with the terminator"; @@ -1030,6 +1032,18 @@ LogicalResult InitialOp::verify() { << " is expected but got " << lhs; } + auto blockArgs = this->getBody().front().getArguments(); + + if (blockArgs.size() != getNumOperands()) + return emitError() << "operand type doesn't match with the block arg"; + + for (auto [blockArg, operand] : llvm::zip(blockArgs, getOperands())) { + if (blockArg.getType() != + cast(operand.getType()).getInnerType()) + return emitError() + << blockArg.getType() << " is expected but got " + << cast(operand.getType()).getInnerType(); + } return success(); } void InitialOp::build(OpBuilder &builder, OperationState &result, @@ -1076,6 +1090,101 @@ Value circt::seq::unwrapImmutableValue(TypedValue value) { return initialOp.getBodyBlock()->getTerminator()->getOperand(resultNum); } +FailureOr circt::seq::mergeInitialOps(Block *block) { + SmallVector initialOps; + for (auto &op : *block) + if (isa(op)) + initialOps.push_back(&op); + + if (!mlir::computeTopologicalSorting(initialOps, {})) + return block->getParentOp()->emitError() << "initial ops cannot be " + << "topologically sorted"; + + // No need to merge if there is only one initial op. + if (initialOps.size() <= 1) + return initialOps.empty() ? seq::InitialOp() + : cast(initialOps[0]); + + auto initialOp = cast(initialOps.front()); + auto yieldOp = cast(initialOp.getBodyBlock()->getTerminator()); + + llvm::MapVector + resultToYieldOperand; // seq.immutable value to operand. + + for (auto [result, operand] : + llvm::zip(initialOp.getResults(), yieldOp->getOperands())) + resultToYieldOperand.insert({result, operand}); + + for (size_t i = 1; i < initialOps.size(); ++i) { + auto currentInitialOp = cast(initialOps[i]); + auto operands = currentInitialOp->getOperands(); + for (auto [blockArg, operand] : + llvm::zip(currentInitialOp.getBodyBlock()->getArguments(), operands)) { + if (auto initOp = operand.getDefiningOp()) { + assert(resultToYieldOperand.count(operand) && + "it must be visited already"); + blockArg.replaceAllUsesWith(resultToYieldOperand.lookup(operand)); + } else { + // Otherwise add the operand to the current block. + initialOp.getBodyBlock()->addArgument( + cast(operand.getType()).getInnerType(), + operand.getLoc()); + initialOp.getInputsMutable().append(operand); + } + } + + auto currentYieldOp = + cast(currentInitialOp.getBodyBlock()->getTerminator()); + + for (auto [result, operand] : llvm::zip(currentInitialOp.getResults(), + currentYieldOp->getOperands())) + resultToYieldOperand.insert({result, operand}); + + // Append the operands of the current yield op to the original yield op. + yieldOp.getOperandsMutable().append(currentYieldOp.getOperands()); + currentYieldOp->erase(); + + // Append the operations of the current initial op to the original initial + // op. + initialOp.getBodyBlock()->getOperations().splice( + initialOp.end(), currentInitialOp.getBodyBlock()->getOperations()); + } + + // Move the terminator to the end of the block. + yieldOp->moveBefore(initialOp.getBodyBlock(), + initialOp.getBodyBlock()->end()); + + auto builder = OpBuilder::atBlockBegin(block); + SmallVector types; + for (auto [result, operand] : resultToYieldOperand) + types.push_back(operand.getType()); + + // Create a new initial op which accumulates the results of the merged initial + // ops. + auto newInitial = builder.create(initialOp.getLoc(), types); + newInitial.getInputsMutable().append(initialOp.getInputs()); + + for (auto [resultAndOperand, newResult] : + llvm::zip(resultToYieldOperand, newInitial.getResults())) + resultAndOperand.first.replaceAllUsesWith(newResult); + + // Update the block arguments of the new initial op. + for (auto oldBlockArg : initialOp.getBodyBlock()->getArguments()) { + auto blockArg = newInitial.getBodyBlock()->addArgument( + oldBlockArg.getType(), oldBlockArg.getLoc()); + oldBlockArg.replaceAllUsesWith(blockArg); + } + + newInitial.getBodyBlock()->getOperations().splice( + newInitial.end(), initialOp.getBodyBlock()->getOperations()); + + // Clean up. + while (!initialOps.empty()) + initialOps.pop_back_val()->erase(); + + return newInitial; +} + //===----------------------------------------------------------------------===// // TableGen generated logic. //===----------------------------------------------------------------------===// diff --git a/test/Conversion/ConvertToArcs/convert-to-arcs.mlir b/test/Conversion/ConvertToArcs/convert-to-arcs.mlir index 29ecfaa0ea63..b874344f6db5 100644 --- a/test/Conversion/ConvertToArcs/convert-to-arcs.mlir +++ b/test/Conversion/ConvertToArcs/convert-to-arcs.mlir @@ -121,15 +121,15 @@ hw.module.extern private @Reshuffling2(out z0: i4, out z1: i4, out z2: i4, out z // CHECK-LABEL: hw.module @ReshufflingInit hw.module @ReshufflingInit(in %clockA: !seq.clock, in %clockB: !seq.clock, out z0: i4, out z1: i4, out z2: i4, out z3: i4) { // CHECK-NEXT: hw.instance "x" @Reshuffling2() - // CHECK-NEXT: %[[INITIAL:.+]]:3 = seq.initial { + // CHECK-NEXT: %[[INITIAL:.+]]:3 = seq.initial() { // CHECK-NEXT: %c1_i4 = hw.constant 1 : i4 // CHECK-NEXT: %c2_i4 = hw.constant 2 : i4 // CHECK-NEXT: %c3_i4 = hw.constant 3 : i4 // CHECK-NEXT: seq.yield %c1_i4, %c2_i4, %c3_i4 : i4, i4, i4 - // CHECK-NEXT: } : !seq.immutable, !seq.immutable, !seq.immutable - // CHECK-NEXT: %[[C1:.+]] = builtin.unrealized_conversion_cast %[[INITIAL]]#0 : !seq.immutable to i4 - // CHECK-NEXT: %[[C2:.+]] = builtin.unrealized_conversion_cast %[[INITIAL]]#1 : !seq.immutable to i4 - // CHECK-NEXT: %[[C3:.+]] = builtin.unrealized_conversion_cast %[[INITIAL]]#2 : !seq.immutable to i4 + // CHECK-NEXT: } : () -> (!seq.immutable, !seq.immutable, !seq.immutable) + // CHECK-NEXT: %[[C1:.+]] = seq.from_immutable %[[INITIAL]]#0 + // CHECK-NEXT: %[[C2:.+]] = seq.from_immutable %[[INITIAL]]#1 + // CHECK-NEXT: %[[C3:.+]] = seq.from_immutable %[[INITIAL]]#2 // CHECK-NEXT: %[[C0:.+]] = hw.constant 0 : i4 // CHECK-NEXT: arc.state @ReshufflingInit_arc(%x.z0, %x.z1) clock %clockA initial (%[[C0]], %[[C1]] : i4, i4) latency 1 // CHECK-NEXT: arc.state @ReshufflingInit_arc_0(%x.z2, %x.z3) clock %clockB initial (%[[C2]], %[[C3]] : i4, i4) latency 1 @@ -137,12 +137,12 @@ hw.module @ReshufflingInit(in %clockA: !seq.clock, in %clockB: !seq.clock, out z %x.z0, %x.z1, %x.z2, %x.z3 = hw.instance "x" @Reshuffling2() -> (z0: i4, z1: i4, z2: i4, z3: i4) %4 = seq.compreg %x.z0, %clockA : i4 - %init0, %init1, %init2 = seq.initial { + %init0, %init1, %init2 = seq.initial () { %cst1 = hw.constant 1 : i4 %cst2 = hw.constant 2 : i4 %cst3 = hw.constant 3 : i4 seq.yield %cst1, %cst2, %cst3 : i4, i4, i4 - } : !seq.immutable, !seq.immutable, !seq.immutable + } : () -> (!seq.immutable, !seq.immutable, !seq.immutable) %5 = seq.compreg %x.z1, %clockA initial %init0 : i4 %6 = seq.compreg %x.z2, %clockB initial %init1 : i4 %7 = seq.compreg %x.z3, %clockB initial %init2 : i4 @@ -242,15 +242,15 @@ hw.module @Trivial(in %clock: !seq.clock, in %i0: i4, in %reset: i1, out out: i4 // CHECK-LABEL: hw.module @TrivialWithInit( hw.module @TrivialWithInit(in %clock: !seq.clock, in %i0: i4, in %reset: i1, out out: i4) { - // CHECK: %[[INIT:.+]] = seq.initial { - // CHECK: %[[CAST:.+]] = builtin.unrealized_conversion_cast %[[INIT]] + // CHECK: %[[INIT:.+]] = seq.initial() { + // CHECK: %[[CAST:.+]] = seq.from_immutable %[[INIT]] // CHECK: [[RES0:%.+]] = arc.state @[[TRIVIALINIT_ARC]](%i0) clock %clock reset %reset initial (%[[CAST]] : i4) latency 1 {names = ["foo"] // CHECK-NEXT: hw.output [[RES0:%.+]] %0 = hw.constant 0 : i4 - %init = seq.initial { + %init = seq.initial() { %cst2 = hw.constant 2 : i4 seq.yield %cst2: i4 - } : !seq.immutable + } : () -> !seq.immutable %foo = seq.compreg %i0, %clock reset %reset, %0 initial %init: i4 hw.output %foo : i4 } diff --git a/test/Conversion/HWToBTOR2/init.mlir b/test/Conversion/HWToBTOR2/init.mlir index b133816207cc..69a03d6c9ac7 100644 --- a/test/Conversion/HWToBTOR2/init.mlir +++ b/test/Conversion/HWToBTOR2/init.mlir @@ -10,10 +10,10 @@ module { //CHECK: [[NID3:[0-9]+]] constd [[NID0]] 0 %false = hw.constant false //CHECK: [[INIT:[0-9]+]] init [[NID0]] [[NID2]] [[NID3]] - %init = seq.initial { + %init = seq.initial() { %false_0 = hw.constant false seq.yield %false_0 : i1 - } : !seq.immutable + } : () -> !seq.immutable //CHECK: [[RESET:[0-9]+]] constd [[NID0]] 0 %reg = seq.compreg %false, %clock reset %reset, %false initial %init : i1 diff --git a/test/Conversion/LTLToCore/assertproperty.mlir b/test/Conversion/LTLToCore/assertproperty.mlir index b49263007032..e20aa9c53ab0 100644 --- a/test/Conversion/LTLToCore/assertproperty.mlir +++ b/test/Conversion/LTLToCore/assertproperty.mlir @@ -6,10 +6,10 @@ module { hw.module @test(in %clock : !seq.clock, in %reset : i1, in %a : i1) { //CHECK: [[CLK:%.+]] = seq.from_clock %clock %0 = seq.from_clock %clock - // CHECK-NEXT: %[[INIT:.+]] = seq.initial { + // CHECK-NEXT: %[[INIT:.+]] = seq.initial() { // CHECK-NEXT: %false = hw.constant false // CHECK-NEXT: seq.yield %false : i1 - // CHECK-NEXT: } : !seq.immutable + // CHECK-NEXT: } : () -> !seq.immutable //CHECK: %true = hw.constant true //CHECK: [[TMP:%.+]] = comb.or %reset, %hbr : i1 diff --git a/test/Conversion/PipelineToHW/test_poweron.mlir b/test/Conversion/PipelineToHW/test_poweron.mlir index d9f311a8a9b8..0e562e477776 100644 --- a/test/Conversion/PipelineToHW/test_poweron.mlir +++ b/test/Conversion/PipelineToHW/test_poweron.mlir @@ -7,10 +7,10 @@ // CHECK: %[[VAL_7:.*]] = seq.compreg sym @p0_stage0_reg1 %[[VAL_0]], %[[VAL_3]] : i32 // CHECK: %[[VAL_8:.*]] = hw.constant false // CHECK: %[[VAL_9:.*]] = seq.compreg sym @p0_stage1_enable %[[VAL_2]], %[[VAL_3]] reset %[[VAL_4]], %[[VAL_8]] initial %[[INIT:.+]] : i1 -// CHECK: %[[INIT]] = seq.initial { +// CHECK: %[[INIT]] = seq.initial() { // CHECK: %false_0 = hw.constant false // CHECK: seq.yield %false_0 : i1 -// CHECK: } : !seq.immutable +// CHECK: } : () -> !seq.immutable // CHECK: %[[VAL_10:.*]] = comb.add %[[VAL_6]], %[[VAL_7]] : i32 // CHECK: hw.output %[[VAL_10]], %[[VAL_9]] : i32, i1 // CHECK: } diff --git a/test/Conversion/SeqToSV/error.mlir b/test/Conversion/SeqToSV/error.mlir new file mode 100644 index 000000000000..cc10e5f6084c --- /dev/null +++ b/test/Conversion/SeqToSV/error.mlir @@ -0,0 +1,19 @@ +// RUN: circt-opt %s -verify-diagnostics --lower-seq-to-sv + +// TODO: Improve the error message +// expected-error @+1 {{initial ops cannot be topologically sorted}} +hw.module @toposort_failure(in %clk: !seq.clock, in %rst: i1, in %i: i32) { + %init = seq.initial (%add) { + ^bb0(%arg0: i32): + seq.yield %arg0 : i32 + } : (!seq.immutable) -> !seq.immutable + + %add = seq.initial (%init) { + ^bb0(%arg0 : i32): + seq.yield %arg0 : i32 + } : (!seq.immutable) -> !seq.immutable + + %reg = seq.compreg %i, %clk initial %init : i32 + %reg2 = seq.compreg %i, %clk initial %add : i32 +} + diff --git a/test/Dialect/Arc/lower-state-errors.mlir b/test/Dialect/Arc/lower-state-errors.mlir index cd6135a3e072..50edab4afdfd 100644 --- a/test/Dialect/Arc/lower-state-errors.mlir +++ b/test/Dialect/Arc/lower-state-errors.mlir @@ -22,3 +22,18 @@ hw.module @argInit(in %clk: !seq.clock, in %input: i42) { %0 = arc.state @DummyArc(%0) clock %clk latency 1 : (i42) -> i42 %1 = arc.state @DummyArc(%1) clock %clk initial (%0 : i42) latency 1 : (i42) -> i42 } + +// ----- + +// expected-error @+1 {{initial ops cannot be topologically sorted}} +hw.module @toposort_failure(in %clk: !seq.clock, in %rst: i1, in %i: i32) { + %init = seq.initial (%add) { + ^bb0(%arg0: i32): + seq.yield %arg0 : i32 + } : (!seq.immutable) -> !seq.immutable + + %add = seq.initial (%init) { + ^bb0(%arg0 : i32): + seq.yield %arg0 : i32 + } : (!seq.immutable) -> !seq.immutable +} diff --git a/test/Dialect/Arc/lower-state.mlir b/test/Dialect/Arc/lower-state.mlir index 1091b99f9a79..d4940137b58c 100644 --- a/test/Dialect/Arc/lower-state.mlir +++ b/test/Dialect/Arc/lower-state.mlir @@ -424,19 +424,22 @@ hw.module @seqInitial(in %clk : i1, out o1 : i8, out o2 : i8) { // CHECK-NEXT: %c5_i8 = hw.constant 5 : i8 // CHECK-NEXT: %[[RAND:.+]] = func.call @random() : () -> i32 // CHECK-NEXT: %[[EXTRACT:.+]] = comb.extract %[[RAND]] from 0 : (i32) -> i8 - // CHECK-NEXT: %[[VAL1:.+]] = builtin.unrealized_conversion_cast %[[EXTRACT]] : i8 to i8 // CHECK-NEXT: arc.state_write %[[STATE1]] = %[[VAL1:.+]] : - // CHECK-NEXT: %[[VAL2:.+]] = builtin.unrealized_conversion_cast %c5_i8 : i8 to i8 // CHECK-NEXT: arc.state_write %[[STATE2]] = %[[VAL2:.+]] : // CHECK-NEXT: } - %0 = builtin.unrealized_conversion_cast %2#0 : !seq.immutable to i8 - %1 = builtin.unrealized_conversion_cast %2#1 : !seq.immutable to i8 - %2:2 = seq.initial { + %0 = seq.from_immutable %7 : (!seq.immutable) -> i8 + %1 = seq.from_immutable %2#1 : (!seq.immutable) -> i8 + %2:2 = seq.initial() { %c5_i8 = hw.constant 5 : i8 %6 = func.call @random() : () -> i32 - %7 = comb.extract %6 from 0 : (i32) -> i8 - seq.yield %7, %c5_i8 : i8, i8 - } : !seq.immutable, !seq.immutable + seq.yield %6, %c5_i8 : i32, i8 + } : () -> (!seq.immutable, !seq.immutable) + %7 = seq.initial(%2#0) { + ^bb0(%arg0 : i32): + %ext = comb.extract %arg0 from 0 : (i32) -> i8 + seq.yield %ext: i8 + } : (!seq.immutable) -> (!seq.immutable) + %3 = arc.state @counter_arc(%3) clock %4 initial (%0 : i8) latency 1 : (i8) -> i8 %4 = arc.call @counter_arc_0(%clk) : (i1) -> !seq.clock %5 = arc.state @counter_arc(%5) clock %4 initial (%1 : i8) latency 1 : (i8) -> i8 diff --git a/test/Dialect/Seq/compreg.mlir b/test/Dialect/Seq/compreg.mlir index 772e26ed662e..19e13aa74cc5 100644 --- a/test/Dialect/Seq/compreg.mlir +++ b/test/Dialect/Seq/compreg.mlir @@ -72,10 +72,10 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1, in %i: i32, in %s: !hw.struct + } : () -> !seq.immutable %c0_i32 = hw.constant 0 : i32 @@ -85,10 +85,10 @@ hw.module @top(in %clk: !seq.clock, in %rst: i1, in %i: i32, in %s: !hw.struct + } : () -> !seq.immutable %r0 = seq.compreg.ce %i, %clk, %ce reset %rst, %rv : i32 // CHECK: %r0 = seq.compreg.ce %i, %clk, %ce reset %rst, %c0_i32 : i32 @@ -137,18 +137,41 @@ hw.module @reg_of_clock_type(in %clk: !seq.clock, in %rst: i1, in %i: !seq.clock hw.output %r1 : !seq.clock } -hw.module @init_with_call(in %clk: !seq.clock, in %rst: i1, in %i: i32, in %s: !hw.struct) { +hw.module @init_with_call(in %clk: !seq.clock, in %rst: i1, in %i: i32, in %s: !hw.struct, out o: i32) { // SV: sv.initial { // SV-NEXT: [[V0:%.+]] = sv.system "random"() : () -> i32 - // SV-NEXT: sv.bpassign %reg, [[V0]] : i32 + // SV-NEXT: [[C:%.+]] = hw.constant 0 + // SV-NEXT: [[MUX:%.+]] = comb.mux %rst, [[C]], [[V0]] + // SV-NEXT: [[V1:%.+]] = comb.add [[MUX]], [[MUX]] : i32 + // SV-NEXT: sv.bpassign %reg, [[MUX]] : i32 + // SV-NEXT: sv.bpassign %reg2, [[V1]] : i32 + // SV-NEXT: sv.bpassign [[REG:%.+]], [[V1]] : i32 // SV-NEXT: } - %init = seq.initial { + %reset_immut = seq.get_initial_value %rst : (i1) -> (!seq.immutable) + %init = seq.initial (%reset_immut) { + ^bb0(%r: i1): %rand = sv.system "random"() : () -> i32 - seq.yield %rand : i32 - } : !seq.immutable + %c0_i32 = hw.constant 0 : i32 + %mux = comb.mux %r, %c0_i32, %rand : i32 + seq.yield %mux : i32 + } : (!seq.immutable) -> !seq.immutable + + %add = seq.initial (%init) { + ^bb0(%arg0 : i32): + %0 = comb.add %arg0, %arg0: i32 + seq.yield %0 : i32 + } : (!seq.immutable) -> !seq.immutable // SV: %reg = sv.reg : !hw.inout %c0_i32 = hw.constant 0 : i32 %reg = seq.compreg %i, %clk initial %init : i32 + %reg2 = seq.compreg %i, %clk initial %add : i32 + + %add_from_immut = seq.from_immutable %add: (!seq.immutable) -> i32 + // SV: [[REG]] = sv.reg + // SV-NEXT: [[RESULT:%.+]] = sv.read_inout [[REG]] + // SV-NEXT: hw.output [[RESULT]] + + hw.output %add_from_immut: i32 } diff --git a/test/Dialect/Seq/errors.mlir b/test/Dialect/Seq/errors.mlir index 5a547ba32193..270f04d02b1e 100644 --- a/test/Dialect/Seq/errors.mlir +++ b/test/Dialect/Seq/errors.mlir @@ -23,18 +23,18 @@ hw.module @fifo3(in %clk : !seq.clock, in %rst : i1, in %in : i32, in %rdEn : i1 hw.module @init() { // expected-error @+1 {{result type doesn't match with the terminator}} - %0 = seq.initial { + %0 = seq.initial () { %1 = hw.constant 32: i32 seq.yield %1, %1: i32, i32 - }: !seq.immutable + }: () -> !seq.immutable } // ----- hw.module @init() { // expected-error @+1 {{'i32' is expected but got 'i16'}} - %0 = seq.initial { + %0 = seq.initial () { %1 = hw.constant 32: i16 seq.yield %1: i16 - }: !seq.immutable + }: () -> !seq.immutable } diff --git a/test/Dialect/Seq/round-trip.mlir b/test/Dialect/Seq/round-trip.mlir index eae52047d482..51f2372266f4 100644 --- a/test/Dialect/Seq/round-trip.mlir +++ b/test/Dialect/Seq/round-trip.mlir @@ -100,9 +100,18 @@ hw.module @clock_inv(in %clock: !seq.clock) { // CHECK-LABEL: @init hw.module @init() { - // CHECK-NEXT: seq.initial - %0 = seq.initial { + // CHECK-NEXT: %[[VAL:.+]] = seq.initial() + %0 = seq.initial () { %1 = hw.constant 32: i32 seq.yield %1: i32 - }: !seq.immutable + }: () -> !seq.immutable + + // CHECK: seq.initial(%[[VAL]]) { + // CHECK-NEXT: ^bb0(%arg0: i32): + // CHECK-NEXT: seq.yield %arg0 : i32 + %1 = seq.initial (%0) { + ^bb0(%arg0: i32): + seq.yield %arg0: i32 + }: (!seq.immutable) -> !seq.immutable + } diff --git a/test/Dialect/Seq/shiftreg.mlir b/test/Dialect/Seq/shiftreg.mlir index 931da38c4b87..55fc08ff7498 100644 --- a/test/Dialect/Seq/shiftreg.mlir +++ b/test/Dialect/Seq/shiftreg.mlir @@ -7,10 +7,10 @@ // LO: %r0_sh1 = seq.compreg.ce sym @r0_sh1 %i, %clk, %ce : i32 // LO: %r0_sh2 = seq.compreg.ce sym @r0_sh2 %r0_sh1, %clk, %ce : i32 // LO: %r0_sh3 = seq.compreg.ce sym @r0_sh3 %r0_sh2, %clk, %ce : i32 -// LO %0 = seq.initial { +// LO %0 = seq.initial () { // LO %c0_i32_0 = hw.constant 0 : i32 // LO seq.yield %c0_i32_0 : i32 -// LO } : !seq.immutable +// LO } : () -> !seq.immutable // LO: %myShiftReg_sh1 = seq.compreg.ce sym @myShiftReg_sh1 %i, %clk, %ce reset %rst, %c0_i32 initial %0 : i32 // LO: %myShiftReg_sh2 = seq.compreg.ce sym @myShiftReg_sh2 %myShiftReg_sh1, %clk, %ce reset %rst, %c0_i32 initial %0 : i32 // LO: %myShiftReg_sh3 = seq.compreg.ce sym @myShiftReg_sh3 %myShiftReg_sh2, %clk, %ce reset %rst, %c0_i32 initial %0 : i32 diff --git a/test/Tools/circt-bmc/externalize-registers-errors.mlir b/test/Tools/circt-bmc/externalize-registers-errors.mlir index 2371c30c07a5..16bab8b2bd9b 100644 --- a/test/Tools/circt-bmc/externalize-registers-errors.mlir +++ b/test/Tools/circt-bmc/externalize-registers-errors.mlir @@ -28,10 +28,10 @@ hw.module @reg_with_reset(in %clk: !seq.clock, in %rst: i1, in %in: i32, out out // ----- hw.module @reg_with_initial(in %clk: !seq.clock, in %in: i32, out out: i32) { - %init = seq.initial { + %init = seq.initial () { %c0_i32 = hw.constant 0 : i32 seq.yield %c0_i32 : i32 - } : !seq.immutable + } : () -> !seq.immutable // expected-error @below {{registers with initial values not yet supported}} %1 = seq.compreg %in, %clk initial %init : i32