Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Arc] Add InitialOp and lowering support for FirReg preset values. #7480

Merged
merged 20 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
2 changes: 1 addition & 1 deletion include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ def ConvertToArcs : Pass<"convert-to-arcs", "mlir::ModuleOp"> {
latency.
}];
let constructor = "circt::createConvertToArcsPass()";
let dependentDialects = ["circt::arc::ArcDialect"];
let dependentDialects = ["circt::arc::ArcDialect", "circt::hw::HWDialect"];
let options = [
Option<"tapRegisters", "tap-registers", "bool", "true",
"Make registers observable">,
Expand Down
54 changes: 39 additions & 15 deletions include/circt/Dialect/Arc/ArcOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ def StateOp : ArcOp<"state", [
DeclareOpInterfaceMethods<SymbolUserOpInterface>,
AttrSizedOperandSegments,
DeclareOpInterfaceMethods<ClockedOpInterface>,
PredOpTrait<"types of initial arguments match result types",
CPred<[{getInitials().empty() ||
llvm::equal(getInitials().getType(), getResults().getType())}]>>
]> {
let summary = "State transfer arc";

Expand All @@ -143,12 +146,13 @@ def StateOp : ArcOp<"state", [
Optional<I1>:$enable,
Optional<I1>:$reset,
I32Attr:$latency,
Variadic<AnyType>:$inputs);
Variadic<AnyType>:$inputs,
Variadic<AnyType>:$initials);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the latency of >1, don't we need even more initials? Like a VariadicOfVariadic because an arc.state with 3 outputs and latency 3 consists of originally 9 registers. I'm not requesting you to change this, just pointing out the (not so big) limitation. (we don't support latency >1 arcs yet anyway)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... this is not something I have considered so far. It makes me again wonder if an initializer region for StateOps would have been the better choice. But I suppose VariadicOfVariadic would also work (can't say I've ever used it though).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably a good idea to revisit this once @uenoku is finished with his register initialization work to make sure it works well with that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly, latency >1 is always a result of some form of optimization within the Arc dialect. We could just declare the initial value on the arc.state op to be used for every step in the latency pipeline, such that the first N cycles will produce the initial value. So basically you don't get to pick different initial values for the different time steps. And then we only merge multiple state ops into a >1 latency one if they have the same initial value.

let results = (outs Variadic<AnyType>:$outputs);

let assemblyFormat = [{
$arc `(` $inputs `)` (`clock` $clock^)? (`enable` $enable^)?
(`reset` $reset^)? `latency` $latency attr-dict
(`reset` $reset^)? ( `initial` ` ` `(` $initials^ `:` type($initials) `)`)? `latency` $latency attr-dict
`:` functional-type($inputs, results)
}];

Expand All @@ -157,21 +161,23 @@ def StateOp : ArcOp<"state", [

let builders = [
OpBuilder<(ins "DefineOp":$arc, "mlir::Value":$clock, "mlir::Value":$enable,
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs), [{
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials), [{
build($_builder, $_state, mlir::SymbolRefAttr::get(arc),
arc.getFunctionType().getResults(), clock, enable, latency,
inputs);
inputs, initials);
}]>,
OpBuilder<(ins "mlir::SymbolRefAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs, CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state, arc, results, clock, enable, Value(), latency,
inputs);
inputs, initials);
}]>,
OpBuilder<(ins "mlir::SymbolRefAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "mlir::Value":$reset,
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs
"unsigned":$latency, CArg<"mlir::ValueRange", "{}">:$inputs,
CArg<"mlir::ValueRange", "{}">:$initials
), [{
if (clock)
$_state.addOperands(clock);
Expand All @@ -180,30 +186,32 @@ def StateOp : ArcOp<"state", [
if (reset)
$_state.addOperands(reset);
$_state.addOperands(inputs);
$_state.addOperands(initials);
$_state.addAttribute("arc", arc);
$_state.addAttribute("latency", $_builder.getI32IntegerAttr(latency));
$_state.addAttribute(getOperandSegmentSizeAttr(),
$_builder.getDenseI32ArrayAttr({
clock ? 1 : 0,
enable ? 1 : 0,
reset ? 1 : 0,
static_cast<int32_t>(inputs.size())}));
static_cast<int32_t>(inputs.size()),
static_cast<int32_t>(initials.size())}));
$_state.addTypes(results);
}]>,
OpBuilder<(ins "mlir::StringAttr":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs, CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state, mlir::SymbolRefAttr::get(arc), results, clock,
enable, latency, inputs);
enable, latency, inputs, initials);
}]>,
OpBuilder<(ins "mlir::StringRef":$arc, "mlir::TypeRange":$results,
"mlir::Value":$clock, "mlir::Value":$enable, "unsigned":$latency,
CArg<"mlir::ValueRange", "{}">:$inputs
CArg<"mlir::ValueRange", "{}">:$inputs, CArg<"mlir::ValueRange", "{}">:$initials
), [{
build($_builder, $_state,
mlir::StringAttr::get($_builder.getContext(), arc),
results, clock, enable, latency, inputs);
results, clock, enable, latency, inputs, initials);
}]>
];
let skipDefaultBuilders = 1;
Expand Down Expand Up @@ -452,6 +460,20 @@ def PassThroughOp : ArcOp<"passthrough", [NoTerminator, NoRegionArguments]> {
}];
}

def InitialOp : ArcOp<"initial", [RecursiveMemoryEffects, NoTerminator, NoRegionArguments]> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add HasParent<"ModelOp"> or are there reasons why we want the additional flexibility?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to restrict it. Again, probably same for PassThroughOp?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I think it also makes sense to add it for PassThroughOp.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks looks like more than 80 cols to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can proudly announce that I have now enabled the vertical ruler for TableGen files in my editor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabianschuiki has probably thought more about this than I since he suggested we'd make this a nested op, but is the only reason this is not isolated from above to access the results of the alloc ops? Are there other use-cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, I shortly had it as isolated from above. But for sake of simplicity and to keep it in line with the PassthroughOp I decided not to keep that. What would be the benefit of making it isolated form above?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you just compute the initial values in this region it doesn't make sense to use SSA values from arbitrary places in the design. Making it isolated provides additional compile-time guarantees. I'd just leave it as is for now.

We are still thinking about how we can better represent the storage rather than hardcoding integer offsets. Maybe we'll unify the alloc ops and storage argument at some point (I have an experimental branch doing that, but it makes use of symbols which is a bit of an issue in verifier time). If something like that makes it in, one block argument would be enough and we could make it isolated from above, but at that point it will be just a small change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With @uenoku's work on initial values in Seq we'll probably get initial values on registers that depend on the same SSA value. I guess with a single initial op you could stuff them all into that one op without any issues. But the idea has come up to make the LowerState pass less monolithic, and possibly break it up into multiple partial lowerings. In that case it might be beneficial to plan for arc.initial ops that can be much smaller and be the result of some partial lowering. For example, each arc.state lowering could generate its own arc.initial op with the initial assignment. In that case it could be beneficial if arc.initial can access SSA values in the parent region? Not sure.

let summary = "Clock-less logic called at the start of simulation";
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = [{
attr-dict-with-keyword $body
}];
let extraClassDeclaration = [{
mlir::Block &getBodyBlock() { return getBody().front(); }
}];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SingleBlock trait auto-generates this if you also rename the body region to bodyRegion (or similar).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thanks. This was just copy pasted from the PassThroughOp, so I guess we could add SingleBlock there, too.

let builders = [
OpBuilder<(ins), "build($_builder, $_state, ValueRange{});">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this builder is not generated by default? What would the ValueRange do if we'd pass one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right That was a left-over from a point where I had InitialOp IsolatedFromAbove and gave it an argument list.

];
}

//===----------------------------------------------------------------------===//
// Storage Allocation
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -652,18 +674,20 @@ def TapOp : ArcOp<"tap"> {
}

def ModelOp : ArcOp<"model", [RegionKindInterface, IsolatedFromAbove,
NoTerminator, Symbol]> {
NoTerminator, Symbol,
DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More than 80 cols?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. 😞

let summary = "A model with stratified clocks";
let description = [{
A model with stratified clocks. The `io` optional attribute
specifies the I/O of the module associated to this model.
}];
let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttrOf<ModuleType>:$io);
TypeAttrOf<ModuleType>:$io,
OptionalAttr<FlatSymbolRefAttr>:$initialFn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SymbolUserOpInterface is necessary if we want to add initializer as a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Missed that one.

let regions = (region SizedRegion<1>:$body);

let assemblyFormat = [{
$sym_name `io` $io attr-dict-with-keyword $body
$sym_name `io` $io (`initializer` $initialFn^)? attr-dict-with-keyword $body
}];

let extraClassDeclaration = [{
Expand Down
6 changes: 4 additions & 2 deletions include/circt/Dialect/Arc/ModelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ struct ModelInfo {
std::string name;
size_t numStateBytes;
llvm::SmallVector<StateInfo> states;
mlir::FlatSymbolRefAttr initialFnSym;

ModelInfo(std::string name, size_t numStateBytes,
llvm::SmallVector<StateInfo> states)
llvm::SmallVector<StateInfo> states,
mlir::FlatSymbolRefAttr initialFnSym)
: name(std::move(name)), numStateBytes(numStateBytes),
states(std::move(states)) {}
states(std::move(states)), initialFnSym(initialFnSym) {}
};

/// Collects information about states within the provided Arc model storage
Expand Down
70 changes: 70 additions & 0 deletions integration_test/arcilator/JIT/initial-shift-reg.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super-nit: remove empty line for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops.

// RUN: arcilator %s --run --jit-entry=main | FileCheck %s
// REQUIRES: arcilator-jit

// CHECK-LABEL: output = ca
// CHECK-NEXT: output = ca
// CHECK-NEXT: output = 0
// CHECK-NEXT: output = fe
// CHECK-NEXT: output = ff

module {

hw.module @shiftreg(in %clock : i1, in %reset : i1, in %en : i1, in %din : i8, out dout : i8) {
%seq_clk = seq.to_clock %clock
%srA = seq.firreg %0 clock %seq_clk preset 0xFE : i8
%srB = seq.firreg %1 clock %seq_clk : i8
%srC = seq.firreg %2 clock %seq_clk preset 0xCA : i8
%0 = comb.mux bin %en, %din, %srA : i8
%1 = comb.mux bin %en, %srA, %srB : i8
%2 = comb.mux bin %en, %srB, %srC : i8
hw.output %srC : i8
}

func.func @main() {
%ff = arith.constant 0xFF : i8
%false = arith.constant 0 : i1
%true = arith.constant 1 : i1

arc.sim.instantiate @shiftreg as %model {
arc.sim.set_input %model, "en" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "reset" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "din" = %ff : i8, !arc.sim.instance<@shiftreg>

%res0 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res0 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>

%res1 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res1 : i8

arc.sim.set_input %model, "en" = %true : i1, !arc.sim.instance<@shiftreg>

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res2 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res2 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res3 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res3 : i8

arc.sim.set_input %model, "clock" = %true : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
arc.sim.set_input %model, "clock" = %false : i1, !arc.sim.instance<@shiftreg>
arc.sim.step %model : !arc.sim.instance<@shiftreg>
%res4 = arc.sim.get_port %model, "dout" : i8, !arc.sim.instance<@shiftreg>
arc.sim.emit "output", %res4 : i8
}
return
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding such a nice integration test! 🎉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is really fantastic!

28 changes: 28 additions & 0 deletions integration_test/arcilator/JIT/initial.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: arcilator %s --run --jit-entry=main 2>&1 >/dev/null | FileCheck %s
// REQUIRES: arcilator-jit

// CHECK: - Init -

module {
llvm.func @_arc_env_get_print_stream(i32) -> !llvm.ptr
llvm.func @_arc_libc_fputs(!llvm.ptr, !llvm.ptr) -> i32
llvm.mlir.global internal constant @global_init_str(" - Init -\0A\00") {addr_space = 0 : i32}

arc.model @initmodel io !hw.modty<> {
^bb0(%arg0: !arc.storage):
arc.passthrough {
%dummy = llvm.mlir.constant(0 : i32) : i32
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite get what this is supposed to test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a ham-fisted way of creating a non-empty block. It's gone now.

arc.initial {
%cst0 = llvm.mlir.constant(0 : i32) : i32
%stderr = llvm.call @_arc_env_get_print_stream(%cst0) : (i32) -> !llvm.ptr
%str = llvm.mlir.addressof @global_init_str : !llvm.ptr
%0 = llvm.call @_arc_libc_fputs(%str, %stderr) : (!llvm.ptr, !llvm.ptr) -> i32
}
Comment on lines +13 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! I guess a future arc.final op could work pretty much in the exact same way, with just a different point in time when it's called. Really neat!

}
func.func @main() {
arc.sim.instantiate @initmodel as %arg0 {
}
return
}
}
14 changes: 13 additions & 1 deletion lib/Conversion/ArcToLLVM/LowerArcToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ namespace {
struct ModelInfoMap {
size_t numStateBytes;
llvm::DenseMap<StringRef, StateInfo> states;
mlir::FlatSymbolRefAttr initialFnSymbol;
};

template <typename OpTy>
Expand Down Expand Up @@ -378,6 +379,16 @@ struct SimInstantiateOpLowering
Value zero =
rewriter.create<LLVM::ConstantOp>(loc, rewriter.getI8Type(), 0);
rewriter.create<LLVM::MemsetOp>(loc, allocated, zero, numStateBytes, false);

// Call the model's 'initial' function if present.
if (model.initialFnSymbol) {
auto initialFnType = LLVM::LLVMFunctionType::get(
LLVM::LLVMVoidType::get(op.getContext()),
{LLVM::LLVMPointerType::get(op.getContext())});
rewriter.create<LLVM::CallOp>(loc, initialFnType, model.initialFnSymbol,
ValueRange{allocated});
}

rewriter.inlineBlockBefore(&adaptor.getBody().getBlocks().front(), op,
{allocated});
rewriter.create<LLVM::CallOp>(loc, freeFunc, ValueRange{allocated});
Expand Down Expand Up @@ -646,7 +657,8 @@ void LowerArcToLLVMPass::runOnOperation() {
for (StateInfo &stateInfo : modelInfo.states)
states.insert({stateInfo.name, stateInfo});
modelMap.insert({modelInfo.name,
ModelInfoMap{modelInfo.numStateBytes, std::move(states)}});
ModelInfoMap{modelInfo.numStateBytes, std::move(states),
modelInfo.initialFnSym}});
}

patterns.add<SimInstantiateOpLowering, SimSetInputOpLowering,
Expand Down
42 changes: 41 additions & 1 deletion lib/Conversion/ConvertToArcs/ConvertToArcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
auto stateOp = dyn_cast<StateOp>(callOp.getOperation());
Value clock = stateOp ? stateOp.getClock() : Value{};
Value reset;
SmallVector<Value> initialValues;
SmallVector<seq::CompRegOp> absorbedRegs;
SmallVector<Attribute> absorbedNames(callOp->getNumResults(), {});
if (auto names = callOp->getAttrOfType<ArrayAttr>("names"))
Expand Down Expand Up @@ -307,6 +308,8 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
}
}

initialValues.push_back(regOp.getPowerOnValue());

absorbedRegs.push_back(regOp);
// If we absorb a register into the arc, the arc effectively produces that
// register's value. So if the register had a name, ensure that we assign
Expand Down Expand Up @@ -345,6 +348,28 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
"had a reset.");
arc.getResetMutable().assign(reset);
}

bool onlyDefaultInitializers =
llvm::all_of(initialValues, [](auto val) -> bool { return !val; });

if (!onlyDefaultInitializers) {
if (!arc.getInitials().empty()) {
return arc.emitError(
"StateOp tried to infer initial values from CompReg, but already "
"had an initial value.");
}
// Create 0 constants for default initialization
for (unsigned i = 0; i < initialValues.size(); ++i) {
if (!initialValues[i]) {
OpBuilder zeroBuilder(arc);
initialValues[i] = zeroBuilder.createOrFold<hw::ConstantOp>(
arc.getLoc(),
zeroBuilder.getIntegerAttr(arc.getResult(i).getType(), 0));
}
}
arc.getInitialsMutable().assign(initialValues);
}

if (tapRegisters && llvm::any_of(absorbedNames, [](auto name) {
return !cast<StringAttr>(name).getValue().empty();
}))
Expand Down Expand Up @@ -385,6 +410,7 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
SmallVector<Value> outputs;
SmallVector<Attribute> names;
SmallVector<Type> types;
SmallVector<Value> initialValues;
SmallDenseMap<Value, unsigned> mapping;
SmallVector<unsigned> regToOutputMapping;
for (auto regOp : regOps) {
Expand All @@ -395,6 +421,7 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
types.push_back(regOp.getType());
outputs.push_back(block->addArgument(regOp.getType(), regOp.getLoc()));
names.push_back(regOp->getAttrOfType<StringAttr>("name"));
initialValues.push_back(regOp.getPowerOnValue());
}
regToOutputMapping.push_back(it->second);
}
Expand All @@ -411,9 +438,22 @@ LogicalResult Converter::absorbRegs(HWModuleOp module) {
defOp.getBody().push_back(block.release());

builder.setInsertionPoint(module.getBodyBlock()->getTerminator());

bool onlyDefaultInitializers =
llvm::all_of(initialValues, [](auto val) -> bool { return !val; });

if (onlyDefaultInitializers)
initialValues.clear();
else
for (unsigned i = 0; i < initialValues.size(); ++i) {
if (!initialValues[i])
initialValues[i] = builder.createOrFold<hw::ConstantOp>(
loc, builder.getIntegerAttr(types[i], 0));
}

auto arcOp =
builder.create<StateOp>(loc, defOp, std::get<0>(clockAndResetAndOp),
/*enable=*/Value{}, 1, inputs);
/*enable=*/Value{}, 1, inputs, initialValues);
auto reset = std::get<1>(clockAndResetAndOp);
if (reset)
arcOp.getResetMutable().assign(reset);
Expand Down
Loading
Loading