From 9e701774db5a051066aca6d552756b81a762a020 Mon Sep 17 00:00:00 2001 From: woshiren Date: Wed, 1 Apr 2026 12:32:18 +0800 Subject: [PATCH 1/2] [ArcToLLVM] Implement sim.clocked_terminate lowering - Implement lowering for `sim.clocked_terminate` by writing a status code (1 for success, 2 for failure) to the state at offset 8. - Increase `kStateOffset` to 16 in `AllocateState.cpp` to reserve space for the termination flag. - Use a branch to the exit block instead of calling `exit()` to allow the simulation instance to terminate without killing the host process. - Add test cases in `sim_clocked_terminate.mlir`. --- lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp | 55 ++++++++++++++++++++ lib/Dialect/Arc/Transforms/AllocateState.cpp | 8 ++- test/Dialect/Arc/allocate-state.mlir | 6 +-- test/Dialect/Arc/lower-sim.mlir | 8 +-- test/arcilator/sim_clocked_terminate.mlir | 25 +++++++++ 5 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 test/arcilator/sim_clocked_terminate.mlir diff --git a/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp b/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp index 039b54da5bcf..d5ae00f826f7 100644 --- a/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp +++ b/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp @@ -987,6 +987,56 @@ struct SimPrintFormattedProcOpLowering StringCache &stringCache; }; +struct SimClockedTerminateOpLowering + : public OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(sim::ClockedTerminateOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto loc = op.getLoc(); + + Value cond = adaptor.getCondition(); + + auto funcOp = op->getParentOfType(); + if (!funcOp || funcOp.getNumArguments() == 0) { + return rewriter.notifyMatchFailure( + op, "Could not find parent LLVM function or state pointer"); + } + + Value statePtr = funcOp.getArgument(0); + + auto *currentBlock = rewriter.getInsertionBlock(); + auto *continueBlock = + rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint()); + auto *setFlagBlock = rewriter.createBlock(continueBlock->getParent()); + + rewriter.setInsertionPointToEnd(currentBlock); + LLVM::CondBrOp::create(rewriter, loc, cond, setFlagBlock, continueBlock); + + rewriter.setInsertionPointToEnd(setFlagBlock); + + auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext()); + auto i8Type = rewriter.getI8Type(); + + Value flagptr = LLVM::GEPOp::create(rewriter, loc, ptrType, i8Type, + statePtr, ArrayRef{8}); + + // Write 1 for success and 2 for failure, so that the runtime can + // distinguish between normal termination and termination due to an error. + int8_t statusVal = op.getSuccess() ? 1 : 2; + Value flagVal = LLVM::ConstantOp::create( + rewriter, loc, i8Type, rewriter.getI8IntegerAttr(statusVal)); + LLVM::StoreOp::create(rewriter, loc, flagVal, flagptr); + + LLVM::BrOp::create(rewriter, loc, continueBlock); + + rewriter.setInsertionPointToStart(continueBlock); + rewriter.eraseOp(op); + return success(); + } +}; + } // namespace static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, @@ -1328,6 +1378,9 @@ void LowerArcToLLVMPass::runOnOperation() { sim::FormatBinOp, sim::FormatOctOp, sim::FormatCharOp, sim::FormatStringConcatOp>(); + // Mark sim::ClockedTerminateOp as illegal + target.addIllegalOp(); + // Setup the arc dialect type conversion. LLVMTypeConverter converter(&getContext()); converter.addConversion([&](seq::ClockType type) { @@ -1429,6 +1482,8 @@ void LowerArcToLLVMPass::runOnOperation() { SimGetPortOpLowering, SimStepOpLowering>( converter, &getContext(), modelMap); + patterns.add(converter, &getContext()); + // Apply the conversion. ConversionConfig config; config.allowPatternRollback = false; diff --git a/lib/Dialect/Arc/Transforms/AllocateState.cpp b/lib/Dialect/Arc/Transforms/AllocateState.cpp index dae96c484a13..66c990b52c52 100644 --- a/lib/Dialect/Arc/Transforms/AllocateState.cpp +++ b/lib/Dialect/Arc/Transforms/AllocateState.cpp @@ -14,9 +14,13 @@ #define DEBUG_TYPE "arc-allocate-state" -// Offset for model state allocations. The first bytes of model storage are +// Offset for model state allocations. The first 8 bytes of model storage are // reserved for the model header (currently just the i64 simulation time). -static constexpr unsigned kStateOffset = 8; +// The next 8 bytes are reserved for the termination flag used by +// `SimTerminateOp`. The actual model state starts at offset 16. +static constexpr unsigned kTimeOffset = 0; +static constexpr unsigned kTerminateFlagOffset = 8; +static constexpr unsigned kStateOffset = 16; namespace circt { namespace arc { diff --git a/test/Dialect/Arc/allocate-state.mlir b/test/Dialect/Arc/allocate-state.mlir index 8fc7de93cf13..8b50561e4608 100644 --- a/test/Dialect/Arc/allocate-state.mlir +++ b/test/Dialect/Arc/allocate-state.mlir @@ -102,16 +102,16 @@ arc.model @test io !hw.modty { } // CHECK-LABEL: arc.model @StructPadding -// CHECK-NEXT: !arc.storage<12> +// CHECK-NEXT: !arc.storage<20> arc.model @StructPadding io !hw.modty<> { ^bb0(%arg0: !arc.storage): - // This !hw.struct is only 11 bits wide, but mapped to an !llvm.struct, each + // This !hw.struct is only 19 bits wide, but mapped to an !llvm.struct, each // field gets byte-aligned. arc.alloc_state %arg0 : (!arc.storage) -> !arc.state> } // CHECK-LABEL: arc.model @ArrayPadding -// CHECK-NEXT: !arc.storage<12> +// CHECK-NEXT: !arc.storage<20> arc.model @ArrayPadding io !hw.modty<> { ^bb0(%arg0: !arc.storage): // This !hw.array is only 18 bits wide, but mapped to an !llvm.array, each diff --git a/test/Dialect/Arc/lower-sim.mlir b/test/Dialect/Arc/lower-sim.mlir index 04e1b547d546..2b2713dde7e6 100644 --- a/test/Dialect/Arc/lower-sim.mlir +++ b/test/Dialect/Arc/lower-sim.mlir @@ -18,16 +18,16 @@ module { // CHECK: %[[format_str_ptr:.*]] = llvm.mlir.addressof @[[format_str]] : !llvm.ptr // CHECK-DAG: %[[c:.*]] = llvm.mlir.constant(24 : i8) // CHECK-DAG: %[[zero:.*]] = llvm.mlir.constant(0 : i8) - // CHECK-DAG: %[[size:.*]] = llvm.mlir.constant(11 : i64) + // CHECK-DAG: %[[size:.*]] = llvm.mlir.constant(19 : i64) // CHECK-DAG: %[[tstep:.*]] = llvm.mlir.constant(321 : i64) // CHECK-DAG: %[[state:.*]] = llvm.call @malloc(%[[size:.*]]) : // CHECK: "llvm.intr.memset"(%[[state]], %[[zero]], %[[size]]) <{isVolatile = false}> arc.sim.instantiate @id as %model { - // CHECK-NEXT: %[[i_ptr:.*]] = llvm.getelementptr %[[state]][8] : (!llvm.ptr) -> !llvm.ptr, i8 + // CHECK-NEXT: %[[i_ptr:.*]] = llvm.getelementptr %[[state]][16] : (!llvm.ptr) -> !llvm.ptr, i8 // CHECK-NEXT: llvm.store %[[c]], %[[i_ptr]] : i8 arc.sim.set_input %model, "i" = %c : i8, !arc.sim.instance<@id> - // CHECK-NEXT: %[[j_ptr:.*]] = llvm.getelementptr %[[state]][9] : (!llvm.ptr) -> !llvm.ptr, i8 + // CHECK-NEXT: %[[j_ptr:.*]] = llvm.getelementptr %[[state]][17] : (!llvm.ptr) -> !llvm.ptr, i8 // CHECK-NEXT: llvm.store %[[c]], %[[j_ptr]] : i8 arc.sim.set_input %model, "j" = %c : i8, !arc.sim.instance<@id> @@ -40,7 +40,7 @@ module { // CHECK-NEXT: llvm.store %[[tnew]], %[[state]] : i64, !llvm.ptr arc.sim.step %model by %tstep : !arc.sim.instance<@id> - // CHECK-NEXT: %[[o_ptr:.*]] = llvm.getelementptr %[[state]][10] : (!llvm.ptr) -> !llvm.ptr, i8 + // CHECK-NEXT: %[[o_ptr:.*]] = llvm.getelementptr %[[state]][18] : (!llvm.ptr) -> !llvm.ptr, i8 // CHECK-NEXT: %[[result:.*]] = llvm.load %[[o_ptr]] : !llvm.ptr -> i8 %result = arc.sim.get_port %model, "o" : i8, !arc.sim.instance<@id> diff --git a/test/arcilator/sim_clocked_terminate.mlir b/test/arcilator/sim_clocked_terminate.mlir new file mode 100644 index 000000000000..1485d26fbc44 --- /dev/null +++ b/test/arcilator/sim_clocked_terminate.mlir @@ -0,0 +1,25 @@ +// RUN: arcilator %s | FileCheck %s + +module { + // CHECK-LABEL: define void @test_success_eval + hw.module @test_success(in %clock: !seq.clock, in %cond: i1) { + // CHECK: br i1 %{{.*}}, label %[[SET_FLAG:.*]], label %[[CONTINUE:.*]] + // CHECK: [[CONTINUE]]: + // CHECK: [[SET_FLAG]]: + // CHECK-NEXT: %[[GEP:.*]] = getelementptr i8, ptr %0, i32 8 + // CHECK-NEXT: store i8 1, ptr %[[GEP]]{{.*}} + // CHECK-NEXT: br label %[[CONTINUE]] + sim.clocked_terminate %clock, %cond, success, verbose + } + + // CHECK-LABEL: define void @test_failure_eval + hw.module @test_failure(in %clock: !seq.clock, in %cond: i1) { + // CHECK: br i1 %{{.*}}, label %[[SET_FLAG_FAIL:.*]], label %[[CONTINUE_FAIL:.*]] + // CHECK: [[CONTINUE_FAIL]]: + // CHECK: [[SET_FLAG_FAIL]]: + // CHECK-NEXT: %[[GEP_FAIL:.*]] = getelementptr i8, ptr %0, i32 8 + // CHECK-NEXT: store i8 2, ptr %[[GEP_FAIL]]{{.*}} + // CHECK-NEXT: br label %[[CONTINUE_FAIL]] + sim.clocked_terminate %clock, %cond, failure, quiet + } +} \ No newline at end of file From 5f4ea012ea9cc85e751e2e6522786fbf121e24ba Mon Sep 17 00:00:00 2001 From: woshiren Date: Thu, 2 Apr 2026 01:30:00 +0800 Subject: [PATCH 2/2] [Arc] lower sim.clocked_terminate to synchronous arc.terminate and LLVM state write --- include/circt/Dialect/Arc/ArcConstants.h | 27 ++++++++++ include/circt/Dialect/Arc/ArcOps.td | 21 ++++++++ lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp | 52 +++++-------------- lib/Dialect/Arc/Transforms/AllocateState.cpp | 9 +--- lib/Dialect/Arc/Transforms/LowerState.cpp | 39 ++++++++++++-- .../ArcToLLVM/lower-arc-to-llvm.mlir | 23 ++++++++ test/Dialect/Arc/lower-state.mlir | 52 +++++++++++++++++++ test/arcilator/sim_clocked_terminate.mlir | 25 --------- 8 files changed, 173 insertions(+), 75 deletions(-) create mode 100644 include/circt/Dialect/Arc/ArcConstants.h delete mode 100644 test/arcilator/sim_clocked_terminate.mlir diff --git a/include/circt/Dialect/Arc/ArcConstants.h b/include/circt/Dialect/Arc/ArcConstants.h new file mode 100644 index 000000000000..ec8e3a0dbe83 --- /dev/null +++ b/include/circt/Dialect/Arc/ArcConstants.h @@ -0,0 +1,27 @@ +//===- ArcConstants.h - Declare Arc dialect constants ------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_ARC_ARCCONSTANTS_H +#define CIRCT_DIALECT_ARC_ARCCONSTANTS_H + +namespace circt { +namespace arc { + +// Offset for model state allocations. The first 16 bytes of model storage are +// reserved for the model header. The first 8 bytes contain the current +// simulation time. The next 8 bytes are reserved for the termination flag used +// by `SimTerminateOp`. The actual model state starts at offset 16. +inline constexpr unsigned kTimeOffset = 0; +inline constexpr unsigned kTerminateFlagOffset = 8; +inline constexpr unsigned kStateOffset = 16; + +} // namespace arc + +} // namespace circt + +#endif // CIRCT_DIALECT_ARC_ARCCONSTANTS_H diff --git a/include/circt/Dialect/Arc/ArcOps.td b/include/circt/Dialect/Arc/ArcOps.td index 265777b78edf..075afbdbaaf0 100644 --- a/include/circt/Dialect/Arc/ArcOps.td +++ b/include/circt/Dialect/Arc/ArcOps.td @@ -626,6 +626,27 @@ def CurrentTimeOp : ArcOp<"current_time", [ }]; } +def TerminateOp : ArcOp<"terminate", [ + MemoryEffects<[MemWrite]> +]> { + let summary = "Request simulation termination"; + let description = [{ + Sets a termination flag in the model's storage. + It unconditionally writes 1 for success or 2 for failure. + This allows the simulation to exit gracefully after the current + evaluation cycle. + }]; + + let arguments = (ins + StorageType:$storage, + BoolAttr:$success + ); + + let assemblyFormat = [{ + $storage `,` $success attr-dict `:` qualified(type($storage)) + }]; +} + //===----------------------------------------------------------------------===// // Procedural Ops //===----------------------------------------------------------------------===// diff --git a/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp b/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp index d5ae00f826f7..0efedc1e8a52 100644 --- a/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp +++ b/lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp @@ -10,6 +10,7 @@ #include "circt/Conversion/CombToArith.h" #include "circt/Conversion/CombToLLVM.h" #include "circt/Conversion/HWToLLVM.h" +#include "circt/Dialect/Arc/ArcConstants.h" #include "circt/Dialect/Arc/ArcOps.h" #include "circt/Dialect/Arc/ModelInfo.h" #include "circt/Dialect/Arc/Runtime/Common.h" @@ -37,6 +38,7 @@ #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/Dialect/SCF/IR/SCF.h" +#include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinDialect.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/DialectConversion.h" @@ -987,51 +989,27 @@ struct SimPrintFormattedProcOpLowering StringCache &stringCache; }; -struct SimClockedTerminateOpLowering - : public OpConversionPattern { +struct TerminateOpLowering : public OpConversionPattern { using OpConversionPattern::OpConversionPattern; LogicalResult - matchAndRewrite(sim::ClockedTerminateOp op, OpAdaptor adaptor, + matchAndRewrite(arc::TerminateOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override { auto loc = op.getLoc(); - Value cond = adaptor.getCondition(); - - auto funcOp = op->getParentOfType(); - if (!funcOp || funcOp.getNumArguments() == 0) { - return rewriter.notifyMatchFailure( - op, "Could not find parent LLVM function or state pointer"); - } - - Value statePtr = funcOp.getArgument(0); - - auto *currentBlock = rewriter.getInsertionBlock(); - auto *continueBlock = - rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint()); - auto *setFlagBlock = rewriter.createBlock(continueBlock->getParent()); - - rewriter.setInsertionPointToEnd(currentBlock); - LLVM::CondBrOp::create(rewriter, loc, cond, setFlagBlock, continueBlock); - - rewriter.setInsertionPointToEnd(setFlagBlock); - - auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext()); auto i8Type = rewriter.getI8Type(); + auto ptrType = LLVM::LLVMPointerType::get(rewriter.getContext()); - Value flagptr = LLVM::GEPOp::create(rewriter, loc, ptrType, i8Type, - statePtr, ArrayRef{8}); + Value flagPtr = LLVM::GEPOp::create( + rewriter, loc, ptrType, i8Type, adaptor.getStorage(), + ArrayRef{arc::kTerminateFlagOffset}); - // Write 1 for success and 2 for failure, so that the runtime can - // distinguish between normal termination and termination due to an error. - int8_t statusVal = op.getSuccess() ? 1 : 2; - Value flagVal = LLVM::ConstantOp::create( - rewriter, loc, i8Type, rewriter.getI8IntegerAttr(statusVal)); - LLVM::StoreOp::create(rewriter, loc, flagVal, flagptr); + uint8_t statusCode = op.getSuccess() ? 1 : 2; + Value codeVal = LLVM::ConstantOp::create( + rewriter, loc, i8Type, rewriter.getI8IntegerAttr(statusCode)); - LLVM::BrOp::create(rewriter, loc, continueBlock); + LLVM::StoreOp::create(rewriter, loc, codeVal, flagPtr); - rewriter.setInsertionPointToStart(continueBlock); rewriter.eraseOp(op); return success(); } @@ -1378,9 +1356,6 @@ void LowerArcToLLVMPass::runOnOperation() { sim::FormatBinOp, sim::FormatOctOp, sim::FormatCharOp, sim::FormatStringConcatOp>(); - // Mark sim::ClockedTerminateOp as illegal - target.addIllegalOp(); - // Setup the arc dialect type conversion. LLVMTypeConverter converter(&getContext()); converter.addConversion([&](seq::ClockType type) { @@ -1456,6 +1431,7 @@ void LowerArcToLLVMPass::runOnOperation() { StateReadOpLowering, StateWriteOpLowering, StorageGetOpLowering, + TerminateOpLowering, TimeToIntOpLowering, ZeroCountOpLowering >(converter, &getContext()); @@ -1482,8 +1458,6 @@ void LowerArcToLLVMPass::runOnOperation() { SimGetPortOpLowering, SimStepOpLowering>( converter, &getContext(), modelMap); - patterns.add(converter, &getContext()); - // Apply the conversion. ConversionConfig config; config.allowPatternRollback = false; diff --git a/lib/Dialect/Arc/Transforms/AllocateState.cpp b/lib/Dialect/Arc/Transforms/AllocateState.cpp index 66c990b52c52..72d67682c088 100644 --- a/lib/Dialect/Arc/Transforms/AllocateState.cpp +++ b/lib/Dialect/Arc/Transforms/AllocateState.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "circt/Dialect/Arc/ArcConstants.h" #include "circt/Dialect/Arc/ArcOps.h" #include "circt/Dialect/Arc/ArcPasses.h" #include "mlir/IR/ImplicitLocOpBuilder.h" @@ -14,14 +15,6 @@ #define DEBUG_TYPE "arc-allocate-state" -// Offset for model state allocations. The first 8 bytes of model storage are -// reserved for the model header (currently just the i64 simulation time). -// The next 8 bytes are reserved for the termination flag used by -// `SimTerminateOp`. The actual model state starts at offset 16. -static constexpr unsigned kTimeOffset = 0; -static constexpr unsigned kTerminateFlagOffset = 8; -static constexpr unsigned kStateOffset = 16; - namespace circt { namespace arc { #define GEN_PASS_DEF_ALLOCATESTATE diff --git a/lib/Dialect/Arc/Transforms/LowerState.cpp b/lib/Dialect/Arc/Transforms/LowerState.cpp index b6bb7ef31cf9..9b949349dfdb 100644 --- a/lib/Dialect/Arc/Transforms/LowerState.cpp +++ b/lib/Dialect/Arc/Transforms/LowerState.cpp @@ -103,6 +103,7 @@ struct OpLowering { LogicalResult lower(seq::InitialOp op); LogicalResult lower(llhd::FinalOp op); LogicalResult lower(llhd::CurrentTimeOp op); + LogicalResult lower(sim::ClockedTerminateOp op); scf::IfOp createIfClockOp(Value clock); @@ -223,7 +224,8 @@ LogicalResult ModuleLowering::run() { // Lower the ops. for (auto &op : moduleOp.getOps()) { - if (mlir::isMemoryEffectFree(&op) && !isa(op)) + if (mlir::isMemoryEffectFree(&op) && + !isa(op)) continue; if (isa(op)) continue; // handled as part of `MemoryOp` @@ -417,8 +419,8 @@ LogicalResult OpLowering::lower() { return TypeSwitch(op) // Operations with special lowering. .Case( - [&](auto op) { return lower(op); }) + seq::InitialOp, llhd::FinalOp, llhd::CurrentTimeOp, + sim::ClockedTerminateOp>([&](auto op) { return lower(op); }) // Operations that should be skipped entirely and never land on the // worklist to be lowered. @@ -1044,6 +1046,37 @@ LogicalResult OpLowering::lower(llhd::CurrentTimeOp op) { return success(); } +LogicalResult OpLowering::lower(sim::ClockedTerminateOp op) { + if (phase != Phase::New) + return success(); + + if (initial) + return success(); + + auto ifClockOp = createIfClockOp(op.getClock()); + if (!ifClockOp) + return failure(); + + OpBuilder::InsertionGuard guard(module.builder); + module.builder.setInsertionPoint(ifClockOp.thenYield()); + + auto loc = op.getLoc(); + Value cond = lowerValue(op.getCondition(), phase); + if (!cond) + return op.emitOpError("Failed to lower condition"); + + auto ifOp = createOrReuseIf(module.builder, cond, false); + if (!ifOp) + return op.emitOpError("Failed to create condition block"); + + module.builder.setInsertionPoint(ifOp.thenYield()); + + arc::TerminateOp::create(module.builder, loc, module.storageArg, + op.getSuccessAttr()); + + return success(); +} + /// Create the operations necessary to detect a posedge on the given clock, /// potentially reusing a previous posedge detection, and create an `scf.if` /// operation for that posedge. This also tries to reuse an `scf.if` operation diff --git a/test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir b/test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir index e6f2e5be2c8b..cb0571924de7 100644 --- a/test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir +++ b/test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir @@ -349,3 +349,26 @@ func.func @Time(%arg0: !arc.storage<42>) -> (i64, !llhd.time, i64) { // CHECK-NEXT: llvm.return [[TMP4]] return %0, %1, %2 : i64, !llhd.time, i64 } + + +// CHECK-LABEL: llvm.func @test_success_eval +// CHECK-SAME: (%[[STATE:.*]]: !llvm.ptr, %[[COND:.*]]: i1) +func.func @test_success_eval(%state: !arc.storage, %cond: i1) { + // CHECK-NEXT: %[[GEP:.*]] = llvm.getelementptr %[[STATE]][8] : (!llvm.ptr) -> !llvm.ptr, i8 + // CHECK-NEXT: %[[VAL:.*]] = llvm.mlir.constant(1 : i8) : i8 + // CHECK-NEXT: llvm.store %[[VAL]], %[[GEP]] : i8, !llvm.ptr + // CHECK-NEXT: llvm.return + arc.terminate %state, true : !arc.storage + return +} + +// CHECK-LABEL: llvm.func @test_failure_eval +// CHECK-SAME: (%[[STATE:.*]]: !llvm.ptr, %[[COND:.*]]: i1) +func.func @test_failure_eval(%state: !arc.storage, %cond: i1) { + // CHECK-NEXT: %[[GEP_FAIL:.*]] = llvm.getelementptr %[[STATE]][8] : (!llvm.ptr) -> !llvm.ptr, i8 + // CHECK-NEXT: %[[VAL_FAIL:.*]] = llvm.mlir.constant(2 : i8) : i8 + // CHECK-NEXT: llvm.store %[[VAL_FAIL]], %[[GEP_FAIL]] : i8, !llvm.ptr + // CHECK-NEXT: llvm.return + arc.terminate %state, false : !arc.storage + return +} diff --git a/test/Dialect/Arc/lower-state.mlir b/test/Dialect/Arc/lower-state.mlir index f1b342ed02ca..a3dfb10895c0 100644 --- a/test/Dialect/Arc/lower-state.mlir +++ b/test/Dialect/Arc/lower-state.mlir @@ -717,3 +717,55 @@ hw.module @LLHDTimeOps(in %clock: !seq.clock, out t: i64) { hw.output %1 : i64 } + +// CHECK-LABEL: arc.model @TestSimToArcTerminateSuccess +// CHECK-SAME: io !hw.modty +hw.module @TestSimToArcTerminateSuccess(in %clock: !seq.clock, in %cond: i1) { + // CHECK: %[[IN_CLK:.*]] = arc.root_input "clock" + // CHECK: %[[IN_COND:.*]] = arc.root_input "cond" + // CHECK: %[[LAST_CLK_PTR:.*]] = arc.alloc_state %arg0 + + // CHECK: %[[CLK_VAL:.*]] = arc.state_read %[[IN_CLK]] : + // CHECK: %[[CURR_CLK:.*]] = seq.from_clock %[[CLK_VAL]] + + // CHECK: %[[LAST_CLK_VAL:.*]] = arc.state_read %[[LAST_CLK_PTR]] : + // CHECK: arc.state_write %[[LAST_CLK_PTR]] = %[[CURR_CLK]] : + + // CHECK: %[[EDGE_XOR:.*]] = comb.xor %[[LAST_CLK_VAL]], %[[CURR_CLK]] : i1 + // CHECK: %[[POSEDGE:.*]] = comb.and %[[EDGE_XOR]], %[[CURR_CLK]] : i1 + + // CHECK: scf.if %[[POSEDGE]] { + // CHECK: %[[COND_VAL:.*]] = arc.state_read %[[IN_COND]] : + // CHECK: scf.if %[[COND_VAL]] { + // CHECK: arc.terminate %arg0, true : !arc.storage + // CHECK: } + // CHECK: } + + sim.clocked_terminate %clock, %cond, success, verbose +} + +// CHECK-LABEL: arc.model @TestSimToArcTerminateFailure +// CHECK-SAME: io !hw.modty +hw.module @TestSimToArcTerminateFailure(in %clock: !seq.clock, in %cond: i1) { + // CHECK: %[[IN_CLK:.*]] = arc.root_input "clock" + // CHECK: %[[IN_COND:.*]] = arc.root_input "cond" + // CHECK: %[[LAST_CLK_PTR:.*]] = arc.alloc_state %arg0 + + // CHECK: %[[CLK_VAL:.*]] = arc.state_read %[[IN_CLK]] : + // CHECK: %[[CURR_CLK:.*]] = seq.from_clock %[[CLK_VAL]] + + // CHECK: %[[LAST_CLK_VAL:.*]] = arc.state_read %[[LAST_CLK_PTR]] : + // CHECK: arc.state_write %[[LAST_CLK_PTR]] = %[[CURR_CLK]] : + + // CHECK: %[[EDGE_XOR:.*]] = comb.xor %[[LAST_CLK_VAL]], %[[CURR_CLK]] : i1 + // CHECK: %[[POSEDGE:.*]] = comb.and %[[EDGE_XOR]], %[[CURR_CLK]] : i1 + + // CHECK: scf.if %[[POSEDGE]] { + // CHECK: %[[COND_VAL:.*]] = arc.state_read %[[IN_COND]] : + // CHECK: scf.if %[[COND_VAL]] { + // CHECK: arc.terminate %arg0, false : !arc.storage + // CHECK: } + // CHECK: } + + sim.clocked_terminate %clock, %cond, failure, verbose +} diff --git a/test/arcilator/sim_clocked_terminate.mlir b/test/arcilator/sim_clocked_terminate.mlir deleted file mode 100644 index 1485d26fbc44..000000000000 --- a/test/arcilator/sim_clocked_terminate.mlir +++ /dev/null @@ -1,25 +0,0 @@ -// RUN: arcilator %s | FileCheck %s - -module { - // CHECK-LABEL: define void @test_success_eval - hw.module @test_success(in %clock: !seq.clock, in %cond: i1) { - // CHECK: br i1 %{{.*}}, label %[[SET_FLAG:.*]], label %[[CONTINUE:.*]] - // CHECK: [[CONTINUE]]: - // CHECK: [[SET_FLAG]]: - // CHECK-NEXT: %[[GEP:.*]] = getelementptr i8, ptr %0, i32 8 - // CHECK-NEXT: store i8 1, ptr %[[GEP]]{{.*}} - // CHECK-NEXT: br label %[[CONTINUE]] - sim.clocked_terminate %clock, %cond, success, verbose - } - - // CHECK-LABEL: define void @test_failure_eval - hw.module @test_failure(in %clock: !seq.clock, in %cond: i1) { - // CHECK: br i1 %{{.*}}, label %[[SET_FLAG_FAIL:.*]], label %[[CONTINUE_FAIL:.*]] - // CHECK: [[CONTINUE_FAIL]]: - // CHECK: [[SET_FLAG_FAIL]]: - // CHECK-NEXT: %[[GEP_FAIL:.*]] = getelementptr i8, ptr %0, i32 8 - // CHECK-NEXT: store i8 2, ptr %[[GEP_FAIL]]{{.*}} - // CHECK-NEXT: br label %[[CONTINUE_FAIL]] - sim.clocked_terminate %clock, %cond, failure, quiet - } -} \ No newline at end of file