Skip to content
Merged
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
60 changes: 53 additions & 7 deletions lib/Dialect/Arc/Transforms/LowerState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct OpLowering {
LogicalResult lower(MemoryOp op);
LogicalResult lower(TapOp op);
LogicalResult lower(InstanceOp op);
LogicalResult lower(hw::TriggeredOp op);
LogicalResult lower(hw::OutputOp op);
LogicalResult lower(seq::InitialOp op);
LogicalResult lower(llhd::FinalOp op);
Expand Down Expand Up @@ -425,9 +426,10 @@ static scf::IfOp createOrReuseIf(OpBuilder &builder, Value condition,
LogicalResult OpLowering::lower() {
return TypeSwitch<Operation *, LogicalResult>(op)
// Operations with special lowering.
.Case<StateOp, sim::DPICallOp, MemoryOp, TapOp, InstanceOp, hw::OutputOp,
seq::InitialOp, llhd::FinalOp, llhd::CurrentTimeOp,
sim::ClockedTerminateOp>([&](auto op) { return lower(op); })
.Case<StateOp, sim::DPICallOp, MemoryOp, TapOp, InstanceOp,
hw::TriggeredOp, hw::OutputOp, 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.
Expand Down Expand Up @@ -846,6 +848,45 @@ LogicalResult OpLowering::lower(InstanceOp op) {
return success();
}

/// Lower `hw.triggered` by inlining its body under a posedge check.
LogicalResult OpLowering::lower(hw::TriggeredOp op) {
assert(phase == Phase::New);

if (op.getEvent() != hw::EventControl::AtPosEdge) {
if (!initial)
return op.emitOpError("only posedge triggers are supported");
return success();
}

lowerValue(op.getTrigger(), Phase::New);
SmallVector<Value> inputs;
for (auto input : op.getInputs())
inputs.push_back(lowerValue(input, Phase::Old));
if (initial)
return success();
if (llvm::is_contained(inputs, Value{}))
return failure();

auto ifClockOp = createIfClockOp(op.getTrigger());
if (!ifClockOp)
return failure();

OpBuilder::InsertionGuard guard(module.builder);
module.builder.setInsertionPoint(ifClockOp.thenYield());

// Expose the trigger inputs as values for the body block arguments.
for (auto [arg, input] : llvm::zip(op.getBodyBlock()->getArguments(), inputs))
module.loweredValues[{arg, Phase::New}] = input;
for (auto &bodyOp : llvm::make_early_inc_range(*op.getBodyBlock())) {
OpLowering bodyLowering(&bodyOp, Phase::New, module);
bodyLowering.initial = false;
if (failed(bodyLowering.lower()))
return failure();
}

return success();
}

/// Lower the main module's outputs by allocating storage for each and then
/// writing the current value into that storage.
LogicalResult OpLowering::lower(hw::OutputOp op) {
Expand Down Expand Up @@ -1105,18 +1146,23 @@ scf::IfOp OpLowering::createIfClockOp(Value clock) {
/// cases. Some operations and values have special handling though. For example,
/// states and memory reads are immediately materialized as a new read op.
Value OpLowering::lowerValue(Value value, Phase phase) {
// Check if the value has already been lowered.
if (auto lowered = module.loweredValues.lookup({value, phase}))
return lowered;

// Handle module inputs. They read the same in all phases.
if (auto arg = dyn_cast<BlockArgument>(value)) {
if (arg.getOwner() != module.moduleOp.getBodyBlock()) {
if (!initial)
emitError(arg.getLoc()) << "block argument has not been lowered";
return {};
}
if (initial)
return {};
auto state = module.allocatedInputs[arg.getArgNumber()];
return StateReadOp::create(module.getBuilder(phase), arg.getLoc(), state);
}

// Check if the value has already been lowered.
if (auto lowered = module.loweredValues.lookup({value, phase}))
return lowered;

// At this point the value is the result of an op. (Block arguments are
// handled above.)
auto result = cast<OpResult>(value);
Expand Down
5 changes: 5 additions & 0 deletions lib/Tools/arcilator/pipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ void circt::populateArcPreprocessingPipeline(

void circt::populateArcConversionPipeline(OpPassManager &pm,
const ArcConversionOptions &options) {
{
sim::SquashSimTriggeredOptions opts;
opts.convertToHW = true;
pm.addNestedPass<hw::HWModuleOp>(sim::createSquashSimTriggered(opts));
}
{
ConvertToArcsPassOptions opts;
opts.tapRegisters = options.observeRegisters;
Expand Down
41 changes: 41 additions & 0 deletions test/Dialect/Arc/lower-state.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,47 @@ hw.module @OpsWithRegions(in %clock: !seq.clock, in %a: i42, in %b: i1, out c: i
hw.output %0 : i42
}

// CHECK-LABEL: arc.model @Triggered
hw.module @Triggered(in %clock: i1, in %a: i42) {
// CHECK: [[IN_CLOCK:%.+]] = arc.root_input "clock"
// CHECK: [[IN_A:%.+]] = arc.root_input "a"
// CHECK: [[A:%.+]] = arc.state_read [[IN_A]]
// CHECK: [[CLOCK:%.+]] = arc.state_read [[IN_CLOCK]]
// CHECK: [[OLD_CLOCK:%.+]] = arc.state_read
// CHECK: arc.state_write
// CHECK: [[EDGE:%.+]] = comb.xor [[OLD_CLOCK]], [[CLOCK]]
// CHECK: [[POSEDGE:%.+]] = comb.and [[EDGE]], [[CLOCK]]
// CHECK: scf.if [[POSEDGE]] {
// CHECK: func.call @ConsumeI42([[A]])
// CHECK: }
hw.triggered posedge %clock(%a) : i42 {
^bb0(%arg: i42):
func.call @ConsumeI42(%arg) : (i42) -> ()
}
}

// CHECK-LABEL: arc.model @TriggeredCurrentTime
hw.module @TriggeredCurrentTime(in %clock: i1) {
// CHECK: [[IN_CLOCK:%.+]] = arc.root_input "clock"
// CHECK: [[CLOCK:%.+]] = arc.state_read [[IN_CLOCK]]
// CHECK: [[OLD_CLOCK:%.+]] = arc.state_read
// CHECK: arc.state_write
// CHECK: [[EDGE:%.+]] = comb.xor [[OLD_CLOCK]], [[CLOCK]]
// CHECK: [[POSEDGE:%.+]] = comb.and [[EDGE]], [[CLOCK]]
// CHECK: scf.if [[POSEDGE]] {
// CHECK: [[TIME_INT:%.+]] = arc.current_time %arg0
// CHECK: [[TIME:%.+]] = llhd.int_to_time [[TIME_INT]]
// CHECK: [[TIME_AS_INT:%.+]] = llhd.time_to_int [[TIME]]
// CHECK: func.call @ConsumeI64([[TIME_AS_INT]])
// CHECK: }
// CHECK-NOT: llhd.current_time
hw.triggered posedge %clock {
%0 = llhd.current_time
%1 = llhd.time_to_int %0
func.call @ConsumeI64(%1) : (i64) -> ()
}
}

// Regression check on worklist producing false positive comb loop errors.
// CHECK-LABEL: @CombLoopRegression
hw.module @CombLoopRegression(in %clk: !seq.clock) {
Expand Down
Loading