diff --git a/lib/Conversion/HandshakeToDC/HandshakeToDC.cpp b/lib/Conversion/HandshakeToDC/HandshakeToDC.cpp index 64e3af77a163..0a42b22bd8f2 100644 --- a/lib/Conversion/HandshakeToDC/HandshakeToDC.cpp +++ b/lib/Conversion/HandshakeToDC/HandshakeToDC.cpp @@ -651,9 +651,9 @@ static hw::ModulePortInfo getModulePortInfoHS(const TypeConverter &tc, return hw::ModulePortInfo{inputs, outputs}; } -class FuncOpConversion : public OpConversionPattern { +class FuncOpConversion : public DCOpConversionPattern { public: - using OpConversionPattern::OpConversionPattern; + using DCOpConversionPattern::DCOpConversionPattern; using OpAdaptor = typename handshake::FuncOp::Adaptor; // Replaces a handshake.func with a hw.module, converting the argument and @@ -668,8 +668,9 @@ class FuncOpConversion : public OpConversionPattern { ModulePortInfo ports = getModulePortInfoHS(*getTypeConverter(), op); if (op.isExternal()) { - rewriter.create( + auto mod = rewriter.create( op.getLoc(), rewriter.getStringAttr(op.getName()), ports); + convertedOps->insert(mod); } else { auto hwModule = rewriter.create( op.getLoc(), rewriter.getStringAttr(op.getName()), ports); @@ -683,6 +684,7 @@ class FuncOpConversion : public OpConversionPattern { (void)getTypeConverter()->convertSignatureArgs( TypeRange(moduleRegion.getArgumentTypes()), result); rewriter.applySignatureConversion(hwModule.getBodyBlock(), result); + convertedOps->insert(hwModule); } rewriter.eraseOp(op); @@ -690,6 +692,32 @@ class FuncOpConversion : public OpConversionPattern { } }; +/// Add DC clock and reset ports to the module. +static void addClkRst(hw::HWModuleOp mod) { + auto *ctx = mod.getContext(); + + size_t numInputs = mod.getNumInputPorts(); + mod.insertInput(numInputs, "clk", seq::ClockType::get(ctx)); + mod.setPortAttrs( + numInputs, + DictionaryAttr::get(ctx, {NamedAttribute(StringAttr::get(ctx, "dc.clock"), + UnitAttr::get(ctx))})); + mod.insertInput(numInputs + 1, "rst", IntegerType::get(ctx, 1)); + mod.setPortAttrs( + numInputs + 1, + DictionaryAttr::get(ctx, {NamedAttribute(StringAttr::get(ctx, "dc.reset"), + UnitAttr::get(ctx))})); + + // We must initialize any port attributes that are not set otherwise the + // verifier will fail. + for (size_t portNum = 0, e = mod.getNumPorts(); portNum < e; ++portNum) { + auto attrs = dyn_cast_or_null(mod.getPortAttrs(portNum)); + if (attrs) + continue; + mod.setPortAttrs(portNum, DictionaryAttr::get(ctx, {})); + } +} + class HandshakeToDCPass : public circt::impl::HandshakeToDCBase { public: @@ -702,8 +730,9 @@ class HandshakeToDCPass auto patternBuilder = [&](TypeConverter &typeConverter, handshaketodc::ConvertedOps &convertedOps, RewritePatternSet &patterns) { - patterns.add(typeConverter, - mod.getContext()); + patterns.add(mod.getContext(), typeConverter, + &convertedOps); + patterns.add(typeConverter, mod.getContext()); }; LogicalResult res = runHandshakeToDC(mod, patternBuilder, targetModifier); @@ -774,5 +803,13 @@ LogicalResult circt::handshaketodc::runHandshakeToDC( // Build any user-specified patterns patternBuilder(typeConverter, convertedOps, patterns); - return applyPartialConversion(op, target, std::move(patterns)); + if (failed(applyPartialConversion(op, target, std::move(patterns)))) + return failure(); + + // Add clock and reset ports to each converted module. + for (auto &op : convertedOps) + if (auto mod = dyn_cast(op); mod) + addClkRst(mod); + + return success(); } diff --git a/test/Conversion/HandshakeToDC/basic.mlir b/test/Conversion/HandshakeToDC/basic.mlir index 8a5071abd1ce..cd1faa69ef23 100644 --- a/test/Conversion/HandshakeToDC/basic.mlir +++ b/test/Conversion/HandshakeToDC/basic.mlir @@ -1,6 +1,6 @@ // RUN: circt-opt %s --lower-handshake-to-dc | FileCheck %s -// CHECK: hw.module @test_fork(in %[[VAL_0:.*]] : !dc.token, out out0 : !dc.token, out out1 : !dc.token) { +// CHECK: hw.module @test_fork(in %[[VAL_0:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.token, out out1 : !dc.token) { // CHECK: %[[VAL_1:.*]]:2 = dc.fork [2] %[[VAL_0]] // CHECK: hw.output %[[VAL_1]]#0, %[[VAL_1]]#1 : !dc.token, !dc.token // CHECK: } @@ -9,7 +9,7 @@ handshake.func @test_fork(%arg0: none) -> (none, none) { return %0#0, %0#1 : none, none } -// CHECK: hw.module @test_fork_data(in %[[VAL_0:.*]] : !dc.value, out out0 : !dc.value) { +// CHECK: hw.module @test_fork_data(in %[[VAL_0:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_1:.*]], %[[VAL_2:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_3:.*]]:2 = dc.fork [2] %[[VAL_1]] // CHECK: %[[VAL_4:.*]] = dc.pack %[[VAL_3]]#0, %[[VAL_2]] : i32 @@ -27,7 +27,7 @@ handshake.func @test_fork_data(%arg0: i32) -> (i32) { return %1 : i32 } -// CHECK: hw.module @top(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.token, out out0 : !dc.value, out out1 : !dc.token) { +// CHECK: hw.module @top(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value, out out1 : !dc.token) { // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_7:.*]] = dc.join %[[VAL_3]], %[[VAL_5]] @@ -47,7 +47,7 @@ handshake.func @top(%arg0: i64, %arg1: i64, %arg8: none, ...) -> (i64, none) { return %1, %arg8 : i64, none } -// CHECK-LABEL: hw.module @constant(out out0 : !dc.value) { +// CHECK-LABEL: hw.module @constant(in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_1:.*]] = dc.source // CHECK: %[[VAL_2:.*]] = arith.constant 42 : i32 // CHECK: %[[VAL_3:.*]] = dc.pack %[[VAL_1]], %[[VAL_2]] : i32 @@ -58,7 +58,7 @@ handshake.func @constant() -> (i32) { return %0 : i32 } -// CHECK: hw.module @mux(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, out out0 : !dc.value) { +// CHECK: hw.module @mux(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = dc.unpack %[[VAL_2]] : !dc.value @@ -75,7 +75,7 @@ handshake.func @mux(%select : i1, %a : i64, %b : i64) -> i64{ return %0 : i64 } -// CHECK: hw.module @mux4(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, in %[[VAL_3:.*]] : !dc.value, in %[[VAL_4:.*]] : !dc.value, out out0 : !dc.value) { +// CHECK: hw.module @mux4(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, in %[[VAL_3:.*]] : !dc.value, in %[[VAL_4:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_9:.*]], %[[VAL_10:.*]] = dc.unpack %[[VAL_2]] : !dc.value @@ -104,7 +104,7 @@ handshake.func @mux4(%select : i2, %a : i64, %b : i64, %c : i64, %d : i64) -> i6 return %0 : i64 } -// CHECK: hw.module @test_conditional_branch(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.token, out out0 : !dc.value, out out1 : !dc.value, out out2 : !dc.token) { +// CHECK: hw.module @test_conditional_branch(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value, out out1 : !dc.value, out out2 : !dc.token) { // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_7:.*]] = dc.join %[[VAL_3]], %[[VAL_5]] @@ -119,7 +119,7 @@ handshake.func @test_conditional_branch(%arg0: i1, %arg1: index, %arg2: none, .. return %0#0, %0#1, %arg2 : index, index, none } -// CHECK: hw.module @test_conditional_branch_none(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.token, out out0 : !dc.token, out out1 : !dc.token) { +// CHECK: hw.module @test_conditional_branch_none(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.token, out out1 : !dc.token) { // CHECK: %[[VAL_2:.*]], %[[VAL_3:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_4:.*]] = dc.join %[[VAL_2]], %[[VAL_1]] // CHECK: %[[VAL_5:.*]] = dc.pack %[[VAL_4]], %[[VAL_3]] : i1 @@ -131,7 +131,7 @@ handshake.func @test_conditional_branch_none(%arg0: i1, %arg1: none) -> (none, n return %0#0, %0#1 : none, none } -// CHECK: hw.module @test_constant(in %[[VAL_0:.*]] : !dc.token, out out0 : !dc.value) { +// CHECK: hw.module @test_constant(in %[[VAL_0:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_1:.*]] = dc.source // CHECK: %[[VAL_2:.*]] = arith.constant 42 : i32 // CHECK: %[[VAL_3:.*]] = dc.pack %[[VAL_1]], %[[VAL_2]] : i32 @@ -142,7 +142,7 @@ handshake.func @test_constant(%arg0: none) -> (i32) { return %1: i32 } -// CHECK: hw.module @test_control_merge(in %[[VAL_0:.*]] : !dc.token, in %[[VAL_1:.*]] : !dc.token, out out0 : !dc.token, out out1 : !dc.value) { +// CHECK: hw.module @test_control_merge(in %[[VAL_0:.*]] : !dc.token, in %[[VAL_1:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.token, out out1 : !dc.value) { // CHECK: %[[VAL_2:.*]] = dc.merge %[[VAL_0]], %[[VAL_1]] // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_2]] : !dc.value // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_2]] : !dc.value @@ -155,7 +155,7 @@ handshake.func @test_control_merge(%arg0 : none, %arg1 : none) -> (none, index) return %out, %idx : none, index } -// CHECK: hw.module @test_control_merge_data(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, out out0 : !dc.value, out out1 : !dc.value) { +// CHECK: hw.module @test_control_merge_data(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value, out out1 : !dc.value) { // CHECK: %[[VAL_2:.*]], %[[VAL_3:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_6:.*]] = dc.merge %[[VAL_2]], %[[VAL_4]] @@ -172,7 +172,7 @@ handshake.func @test_control_merge_data(%arg0 : i2, %arg1 : i2) -> (i2, index) { } // CHECK-LABEL: hw.module @test_control_fixed_index_type(in -// CHECK-SAME: %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, out out0 : !dc.value, out out1 : !dc.value) { +// CHECK-SAME: %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value, out out1 : !dc.value) { // CHECK: %[[VAL_2:.*]], %[[VAL_3:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_6:.*]] = dc.merge %[[VAL_2]], %[[VAL_4]] @@ -188,7 +188,7 @@ handshake.func @test_control_fixed_index_type(%arg0 : i4, %arg1 : i4) -> (i4, i3 return %out, %idx : i4, i32 } -// CHECK: hw.module @branch_and_merge(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.token, out out0 : !dc.token, out out1 : !dc.value) { +// CHECK: hw.module @branch_and_merge(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.token, out out1 : !dc.value) { // CHECK: %[[VAL_2:.*]] = dc.merge %[[VAL_3:.*]], %[[VAL_4:.*]] // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_2]] : !dc.value // CHECK: %[[VAL_7:.*]], %[[VAL_8:.*]] = dc.unpack %[[VAL_2]] : !dc.value @@ -206,7 +206,7 @@ handshake.func @branch_and_merge(%0 : i1, %1 : none) -> (none, index) { return %out, %idx : none, index } -// CHECK: hw.module @datamerge(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, out out0 : !dc.value) { +// CHECK: hw.module @datamerge(in %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %[[VAL_2:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value) { // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_5:.*]], %[[VAL_6:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_7:.*]] = dc.merge %[[VAL_3]], %[[VAL_5]] @@ -220,7 +220,7 @@ handshake.func @datamerge(%arg0 : i1, %arg1 : i1, %arg2 : i1) -> i1 { return %out : i1 } -// CHECK: hw.module @nonemerge(in %[[VAL_0:.*]] : !dc.token, in %[[VAL_1:.*]] : !dc.token, out out0 : !dc.token) { +// CHECK: hw.module @nonemerge(in %[[VAL_0:.*]] : !dc.token, in %[[VAL_1:.*]] : !dc.token, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.token) { // CHECK: %[[VAL_2:.*]] = dc.merge %[[VAL_0]], %[[VAL_1]] // CHECK: %[[VAL_3:.*]], %[[VAL_4:.*]] = dc.unpack %[[VAL_2]] : !dc.value // CHECK: hw.output %[[VAL_3]] : !dc.token @@ -231,7 +231,7 @@ handshake.func @nonemerge(%arg0 : none, %arg1 : none) -> none { } // CHECK-LABEL: hw.module @pack_unpack(in -// CHECK-SAME: %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, out out0 : !dc.value, out out1 : !dc.value) { +// CHECK-SAME: %[[VAL_0:.*]] : !dc.value, in %[[VAL_1:.*]] : !dc.value, in %clk : !seq.clock {dc.clock}, in %rst : i1 {dc.reset}, out out0 : !dc.value, out out1 : !dc.value) { // CHECK: %[[VAL_2:.*]], %[[VAL_3:.*]] = dc.unpack %[[VAL_0]] : !dc.value // CHECK: %[[VAL_4:.*]], %[[VAL_5:.*]] = dc.unpack %[[VAL_1]] : !dc.value // CHECK: %[[VAL_6:.*]] = dc.join %[[VAL_2]], %[[VAL_4]]