From 375018a90ce2c7de0729a837d1f79b6d4b060a8f Mon Sep 17 00:00:00 2001 From: Morten Borup Petersen Date: Thu, 11 Aug 2022 12:40:56 +0200 Subject: [PATCH] [MSFT] Introduce `LinearOp` (#3680) The `msft.hlc.linear` operation defines a linear datapath. It serves as a container for `hw`, `comb` and `msft` operations, with the following restrictions: - Not graph-like - only SSA def-use chains are allowed. - `seq` ops are not allowed. --- include/circt/Dialect/MSFT/MSFTConstructs.td | 34 ++++++++++++++++++++ include/circt/Dialect/MSFT/MSFTOps.td | 2 +- lib/Dialect/MSFT/MSFTOps.cpp | 17 ++++++++++ test/Dialect/MSFT/constructs.mlir | 16 +++++++++ test/Dialect/MSFT/opt-errors.mlir | 12 +++++++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/include/circt/Dialect/MSFT/MSFTConstructs.td b/include/circt/Dialect/MSFT/MSFTConstructs.td index c3d5b773cc..83bb601a4c 100644 --- a/include/circt/Dialect/MSFT/MSFTConstructs.td +++ b/include/circt/Dialect/MSFT/MSFTConstructs.td @@ -67,3 +67,37 @@ def ChannelOp: MSFTOp<"constructs.channel", $input $clk $sym_name `(` $defaultStages `)` attr-dict `:` type($input) }]; } + +// Linear, pipelineable datapath. +def LinearOp : MSFTOp<"hlc.linear", [ + SingleBlockImplicitTerminator<"OutputOp"> + ]> { + let summary = "Model of a linear datapath which can be arbitrarily pipelined"; + let description = [{ + Defines a feed-forward datapath which can be scheduled into a pipeline. + Due to the feed-forwardness, the inner region is NOT a graph region. + Internally, only combinational operations (`comb`, `msft`, `hw`) are allowed. + + Example: + ```mlir + msft.module @foo(%in0 : i32, %in1 : i32, %in2 : i32, %clk : i1) -> (out: i32) -> { + %0 = msft.hlc.linear(%a = %in0, %b = %in1, %c = %in2) clock %clk (i32, i32, i32) -> (i32) { + %0 = comb.mul %a, %b : i32 + %1 = comb.add %0, %c : i32 + msft.output %1 : i32 + } + } + ``` + }]; + + let arguments = (ins I1:$clock); + let results = (outs Variadic:$outs); + let regions = (region SizedRegion<1>:$datapath); + + let extraClassDeclaration = [{ + Block *getBodyBlock() { return &datapath().front(); } + }]; + + let hasVerifier = 1; + let assemblyFormat = [{ `clock` $clock attr-dict `:` type($outs) $datapath }]; +} diff --git a/include/circt/Dialect/MSFT/MSFTOps.td b/include/circt/Dialect/MSFT/MSFTOps.td index aaf4c9d412..53800ece18 100644 --- a/include/circt/Dialect/MSFT/MSFTOps.td +++ b/include/circt/Dialect/MSFT/MSFTOps.td @@ -237,7 +237,7 @@ def DesignPartitionOp : MSFTOp<"partition", let assemblyFormat = "$sym_name `,` $verilogName attr-dict"; } -def OutputOp : MSFTOp<"output", [Terminator, HasParent<"MSFTModuleOp">, +def OutputOp : MSFTOp<"output", [Terminator, ParentOneOf<["MSFTModuleOp", "LinearOp"]>, NoSideEffect, ReturnLike]> { let summary = "termination operation"; diff --git a/lib/Dialect/MSFT/MSFTOps.cpp b/lib/Dialect/MSFT/MSFTOps.cpp index a769b1fba6..f5d10e800a 100644 --- a/lib/Dialect/MSFT/MSFTOps.cpp +++ b/lib/Dialect/MSFT/MSFTOps.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "circt/Dialect/MSFT/MSFTOps.h" +#include "circt/Dialect/Comb/CombDialect.h" #include "circt/Dialect/HW/HWAttributes.h" #include "circt/Dialect/HW/HWOps.h" #include "circt/Dialect/HW/ModuleImplementation.h" @@ -978,5 +979,21 @@ void SystolicArrayOp::print(OpAsmPrinter &p) { p.printRegion(pe(), false); } +//===----------------------------------------------------------------------===// +// LinearOp +//===----------------------------------------------------------------------===// + +LogicalResult LinearOp::verify() { + + for (auto &op : *getBodyBlock()) { + if (!isa( + op.getDialect())) + return emitOpError() << "expected only hw, comb, and msft dialect ops " + "inside the datapath."; + } + + return success(); +} + #define GET_OP_CLASSES #include "circt/Dialect/MSFT/MSFT.cpp.inc" diff --git a/test/Dialect/MSFT/constructs.mlir b/test/Dialect/MSFT/constructs.mlir index 62ac722121..b6dfe74ac7 100644 --- a/test/Dialect/MSFT/constructs.mlir +++ b/test/Dialect/MSFT/constructs.mlir @@ -61,3 +61,19 @@ msft.module @ChannelExample {} (%clk: i1, %a : i8) -> (out: i8) { %out = msft.constructs.channel %a %clk "chEx" (2) : i8 msft.output %out : i8 } + +// CHECK-LABEL: msft.module @foo {} (%in0: i32, %in1: i32, %in2: i32, %clk: i1) -> (out: i32) { +// CHECK: %0 = msft.hlc.linear clock %clk : i32 { +// CHECK: %1 = comb.mul %in0, %in1 : i32 +// CHECK: %2 = comb.add %1, %in2 : i32 +// CHECK: msft.output %2 : i32 +// CHECK: } +// CHECK: msft.output %0 : i32 +msft.module @foo {} (%in0 : i32, %in1 : i32, %in2 : i32, %clk : i1) -> (out: i32) { + %0 = msft.hlc.linear clock %clk : i32 { + %0 = comb.mul %in0, %in1 : i32 + %1 = comb.add %0, %in2 : i32 + msft.output %1 : i32 + } + msft.output %0 : i32 +} diff --git a/test/Dialect/MSFT/opt-errors.mlir b/test/Dialect/MSFT/opt-errors.mlir index 55bb42599e..1655642f61 100644 --- a/test/Dialect/MSFT/opt-errors.mlir +++ b/test/Dialect/MSFT/opt-errors.mlir @@ -45,3 +45,15 @@ msft.module @M {} (%x : i32) { comb.add %x, %x {msft.appid=#msft.appid<"add"[0]>} : i32 msft.output } + +// ----- + +msft.module @foo {} (%in0 : i32, %clk : i1) -> (out: i32) { +// expected-error @+1 {{'msft.hlc.linear' op expected only hw, comb, and msft dialect ops inside the datapath.}} + %0 = msft.hlc.linear clock %clk : i32 { + %c1_i1 = hw.constant 1 : i1 + %0 = seq.compreg %in0, %c1_i1 : i32 + msft.output %0 : i32 + } + msft.output %0 : i32 +}