diff --git a/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp b/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp index 05f358693b88..809853443a69 100644 --- a/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp +++ b/lib/Conversion/ConvertToArcs/ConvertToArcs.cpp @@ -38,7 +38,7 @@ struct Converter { LogicalResult runOnModule(HWModuleOp module); LogicalResult analyzeFanIn(); void extractArcs(HWModuleOp module); - void absorbRegs(HWModuleOp module); + LogicalResult absorbRegs(HWModuleOp module); /// The global namespace used to create unique definition names. Namespace globalNamespace; @@ -101,7 +101,8 @@ LogicalResult Converter::runOnModule(HWModuleOp module) { // Extract the fanin mask groups into separate combinational arcs and // combine them with the registers in the design. extractArcs(module); - absorbRegs(module); + if (failed(absorbRegs(module))) + return failure(); return success(); } @@ -251,13 +252,14 @@ void Converter::extractArcs(HWModuleOp module) { } } -void Converter::absorbRegs(HWModuleOp module) { +LogicalResult Converter::absorbRegs(HWModuleOp module) { // Handle the trivial cases where all of an arc's results are used by // exactly one register each. unsigned outIdx = 0; unsigned numTrivialRegs = 0; for (auto &arc : arcUses) { Value clock = arc.getClock(); + Value reset; SmallVector absorbedRegs; SmallVector absorbedNames(arc.getNumResults(), {}); if (auto names = arc->getAttrOfType("names")) @@ -278,7 +280,27 @@ void Converter::absorbRegs(HWModuleOp module) { isTrivial = false; break; } + clock = regOp.getClk(); + reset = regOp.getReset(); + + // Check that if the register has a reset, it is to a constant zero + if (reset) { + Value resetValue = regOp.getResetValue(); + Operation *op = resetValue.getDefiningOp(); + if (!op) + return regOp->emitOpError( + "is reset by an input; not supported by ConvertToArcs"); + if (auto constant = dyn_cast(op)) { + if (constant.getValue() != 0) + return regOp->emitOpError("is reset to a constant non-zero value; " + "not supported by ConvertToArcs"); + } else { + return regOp->emitOpError("is reset to a value that is not clearly " + "constant; not supported by ConvertToArcs"); + } + } + 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 @@ -294,10 +316,17 @@ void Converter::absorbRegs(HWModuleOp module) { ++numTrivialRegs; // Set the arc's clock to the clock of the registers we've absorbed, bump - // the latency up by one to account for the registers, and update the output - // names. Then replace the registers. + // the latency up by one to account for the registers, add the reset if + // present and update the output names. Then replace the registers. arc.getClockMutable().assign(clock); arc.setLatency(arc.getLatency() + 1); + if (reset) { + if (arc.getReset()) + return arc.emitError( + "StateOp tried to infer reset from CompReg, but already " + "had a reset."); + arc.getResetMutable().assign(reset); + } if (llvm::any_of(absorbedNames, [](auto name) { return !name.template cast().getValue().empty(); })) @@ -315,18 +344,20 @@ void Converter::absorbRegs(HWModuleOp module) { << " regs to arcs\n"); arcUses.truncate(outIdx); - // Group the remaining registers by the operation they use as input. This - // will allow us to generally collapse registers derived from the same arc - // into one shuffling arc. - MapVector, SmallVector> + // Group the remaining registers by their clock, their reset and the operation + // they use as input. This will allow us to generally collapse registers + // derived from the same arc into one shuffling arc. + MapVector, SmallVector> regsByInput; for (auto *op : arcBreakers) - if (auto regOp = dyn_cast_or_null(op)) - regsByInput[{regOp.getClk(), regOp.getInput().getDefiningOp()}].push_back( - regOp); + if (auto regOp = dyn_cast_or_null(op)) { + regsByInput[{regOp.getClk(), regOp.getReset(), + regOp.getInput().getDefiningOp()}] + .push_back(regOp); + } unsigned numMappedRegs = 0; - for (auto [clockAndOp, regOps] : regsByInput) { + for (auto [clockAndResetAndOp, regOps] : regsByInput) { numMappedRegs += regOps.size(); OpBuilder builder(module); auto block = std::make_unique(); @@ -362,8 +393,12 @@ void Converter::absorbRegs(HWModuleOp module) { defOp.getBody().push_back(block.release()); builder.setInsertionPoint(module.getBodyBlock()->getTerminator()); - auto arcOp = builder.create(loc, defOp, clockAndOp.first, Value{}, - 1, inputs); + auto arcOp = + builder.create(loc, defOp, std::get<0>(clockAndResetAndOp), + /*enable=*/Value{}, 1, inputs); + auto reset = std::get<1>(clockAndResetAndOp); + if (reset) + arcOp.getResetMutable().assign(reset); if (llvm::any_of(names, [](auto name) { return !name.template cast().getValue().empty(); })) @@ -377,6 +412,8 @@ void Converter::absorbRegs(HWModuleOp module) { if (numMappedRegs > 0) LLVM_DEBUG(llvm::dbgs() << "- Mapped " << numMappedRegs << " regs to " << regsByInput.size() << " shuffling arcs\n"); + + return success(); } //===----------------------------------------------------------------------===// diff --git a/test/Conversion/ConvertToArcs/convert-to-arcs.mlir b/test/Conversion/ConvertToArcs/convert-to-arcs.mlir index de73412a4e7f..f8f85a50ed9d 100644 --- a/test/Conversion/ConvertToArcs/convert-to-arcs.mlir +++ b/test/Conversion/ConvertToArcs/convert-to-arcs.mlir @@ -181,3 +181,37 @@ hw.module @AbsorbNames(%clock: i1) -> () { // CHECK-NEXT: } hw.module.extern @AbsorbNames2() -> (z0: i4, z1: i4) + +// CHECK: arc.define @[[TRIVIAL_ARC:.+]]([[ARG0:%.+]]: i4) +// CHECK-NEXT: arc.output [[ARG0]] +// CHECK-NEXT: } + +// CHECK-LABEL: hw.module @Trivial( +hw.module @Trivial(%clock: i1, %i0: i4, %reset: i1) -> (out: i4) { + // CHECK: [[RES0:%.+]] = arc.state @[[TRIVIAL_ARC]](%i0) clock %clock reset %reset lat 1 {names = ["foo"] + // CHECK-NEXT: hw.output [[RES0:%.+]] + %0 = hw.constant 0 : i4 + %foo = seq.compreg %i0, %clock, %reset, %0 : i4 + hw.output %foo : i4 +} +// CHECK-NEXT: } + +// CHECK-NEXT: arc.define @[[NONTRIVIAL_ARC_0:.+]]([[ARG0_1:%.+]]: i4) +// CHECK-NEXT: arc.output [[ARG0_1]] +// CHECK-NEXT: } + +// CHECK-NEXT: arc.define @[[NONTRIVIAL_ARC_1:.+]]([[ARG0_2:%.+]]: i4) +// CHECK-NEXT: arc.output [[ARG0_2]] +// CHECK-NEXT: } + +// CHECK-LABEL: hw.module @NonTrivial( +hw.module @NonTrivial(%clock: i1, %i0: i4, %reset1: i1, %reset2: i1) -> (out1: i4, out2: i4) { + // CHECK: [[RES2:%.+]] = arc.state @[[NONTRIVIAL_ARC_0]](%i0) clock %clock reset %reset1 lat 1 {names = ["foo"] + // CHECK-NEXT: [[RES3:%.+]] = arc.state @[[NONTRIVIAL_ARC_1]](%i0) clock %clock reset %reset2 lat 1 {names = ["bar"] + // CHECK-NEXT: hw.output [[RES2]], [[RES3]] + %0 = hw.constant 0 : i4 + %foo = seq.compreg %i0, %clock, %reset1, %0 : i4 + %bar = seq.compreg %i0, %clock, %reset2, %0 : i4 + hw.output %foo, %bar : i4, i4 +} +// CHECK-NEXT: }