circt icon indicating copy to clipboard operation
circt copied to clipboard

[FIRRTL] GCT Views and Dedup

Open seldridge opened this issue 2 years ago • 0 comments

Grand Central (GCT) Views do not currently work when combined with FIRRTL's deduplication pass. Consider the following circuit and annotation file. Top instantiates Foo and Foo_1. Each of those, respectively, instantiate Bar and Bar_1. Each Foo* has a view of something inside each Bar*:

circuit Top :
  module Bar :
    input clock : Clock
    input reset : Reset

    wire w : UInt<1>
    w <= UInt<1>("h0")

  module MyView_companion :

    skip

  module Foo :
    input clock : Clock
    input reset : Reset

    inst bar of Bar
    bar.clock <= clock
    bar.reset <= reset
    inst MyView_companion of MyView_companion

  module Bar_1 :
    input clock : Clock
    input reset : Reset

    wire w : UInt<1>
    w <= UInt<1>("h0")

  module MyView_companion_0 :

    skip

  module Foo_1 :
    input clock : Clock
    input reset : Reset

    inst bar of Bar_1
    bar.clock <= clock
    bar.reset <= reset
    inst MyView_companion_0 of MyView_companion_0

  module Top :
    input clock : Clock
    input reset : UInt<1>

    inst foo1 of Foo
    foo1.clock <= clock
    foo1.reset <= reset
    inst foo2 of Foo_1
    foo2.clock <= clock
    foo2.reset <= reset
[
  {
    "class": "sifive.enterprise.grandcentral.ViewAnnotation",
    "name": "MyView",
    "companion": "~Top|MyView_companion",
    "parent": "~Top|Foo",
    "view": {
      "class": "sifive.enterprise.grandcentral.AugmentedBundleType",
      "defName": "MyInterface",
      "elements": [
        {
          "name": "w",
          "tpe": {
            "class": "sifive.enterprise.grandcentral.AugmentedGroundType",
            "ref": {
              "circuit": "Top",
              "module": "Top",
              "path": [
                {
                  "_1": {
                    "class": "firrtl.annotations.TargetToken$Instance",
                    "value": "foo1"
                  },
                  "_2": {
                    "class": "firrtl.annotations.TargetToken$OfModule",
                    "value": "Foo"
                  }
                },
                {
                  "_1": {
                    "class": "firrtl.annotations.TargetToken$Instance",
                    "value": "bar"
                  },
                  "_2": {
                    "class": "firrtl.annotations.TargetToken$OfModule",
                    "value": "Bar"
                  }
                }
              ],
              "ref": "w",
              "component": []
            },
            "tpe": {
              "class": "sifive.enterprise.grandcentral.GrandCentralView$UnknownGroundType$"
            }
          }
        }
      ]
    }
  },
  {
    "class": "sifive.enterprise.grandcentral.ViewAnnotation",
    "name": "MyView",
    "companion": "~Top|MyView_companion_0",
    "parent": "~Top|Foo_1",
    "view": {
      "class": "sifive.enterprise.grandcentral.AugmentedBundleType",
      "defName": "MyInterface",
      "elements": [
        {
          "name": "w",
          "tpe": {
            "class": "sifive.enterprise.grandcentral.AugmentedGroundType",
            "ref": {
              "circuit": "Top",
              "module": "Top",
              "path": [
                {
                  "_1": {
                    "class": "firrtl.annotations.TargetToken$Instance",
                    "value": "foo2"
                  },
                  "_2": {
                    "class": "firrtl.annotations.TargetToken$OfModule",
                    "value": "Foo_1"
                  }
                },
                {
                  "_1": {
                    "class": "firrtl.annotations.TargetToken$Instance",
                    "value": "bar"
                  },
                  "_2": {
                    "class": "firrtl.annotations.TargetToken$OfModule",
                    "value": "Bar_1"
                  }
                }
              ],
              "ref": "w",
              "component": []
            },
            "tpe": {
              "class": "sifive.enterprise.grandcentral.GrandCentralView$UnknownGroundType$"
            }
          }
        }
      ]
    }
  }
]

If you run this with firtool Foo.fir -annotation-file Foo.anno.json -dedup -firrtl-grand-central you get an expected error. The GCT Views pass is asserting that the NoDedupAnnotation behavior exists:

firrtl/Top.fir:13:10: error: 'firrtl.module' op is marked as a GrandCentral 'parent', but it is instantiated more than once
  module Foo :
         ^
firrtl/Top.fir:13:10: note: see current operation: "firrtl.module"() ({
^bb0(%arg0: !firrtl.clock, %arg1: !firrtl.uint<1>):
  %0:2 = "firrtl.instance"() {annotations = [], inner_sym = #firrtl<"innerSym@bar">, moduleName = @Bar, name = "bar", nameKind = #firrtl<"name_kind droppable_name">, portAnnotations = [[], []], portDirections = 0 : i2, portNames = ["clock", "reset"]} : () -> (!firrtl.clock, !firrtl.uint<1>)
  "firrtl.strictconnect"(%0#0, %arg0) : (!firrtl.clock, !firrtl.clock) -> ()
  "firrtl.strictconnect"(%0#1, %arg1) : (!firrtl.uint<1>, !firrtl.uint<1>) -> ()
  "firrtl.instance"() {annotations = [], inner_sym = #firrtl<"innerSym@MyView_companion">, moduleName = @MyView_companion, name = "MyView_companion", nameKind = #firrtl<"name_kind droppable_name">, portAnnotations = [], portDirections = 0 : i0, portNames = []} : () -> ()
}) {annotations = [{circt.nonlocal = @nla_3, class = "sifive.enterprise.grandcentral.ViewAnnotation.parent", id = 2 : i64, name = "MyView", type = "parent"}, {circt.nonlocal = @nla_4, class = "sifive.enterprise.grandcentral.ViewAnnotation.parent", id = 0 : i64, name = "MyView", type = "parent"}], portAnnotations = [[], []], portDirections = 0 : i2, portNames = ["clock", "reset"], portSyms = ["", ""], portTypes = [!firrtl.clock, !firrtl.uint<1>], sym_name = "Foo", sym_visibility = "private"} : () -> ()
firrtl/Top.fir:49:5: note: parent is instantiated here
    inst foo2 of Foo_1
    ^
firrtl/Top.fir:46:5: note: parent is instantiated here
    inst foo1 of Foo
    ^
firrtl/Top.fir:13:10: error: 'firrtl.module' op is marked as a GrandCentral 'parent', but it is instantiated more than once
  module Foo :
         ^
firrtl/Top.fir:13:10: note: see current operation: "firrtl.module"() ({
^bb0(%arg0: !firrtl.clock, %arg1: !firrtl.uint<1>):
  %0:2 = "firrtl.instance"() {annotations = [], inner_sym = #firrtl<"innerSym@bar">, moduleName = @Bar, name = "bar", nameKind = #firrtl<"name_kind droppable_name">, portAnnotations = [[], []], portDirections = 0 : i2, portNames = ["clock", "reset"]} : () -> (!firrtl.clock, !firrtl.uint<1>)
  "firrtl.strictconnect"(%0#0, %arg0) : (!firrtl.clock, !firrtl.clock) -> ()
  "firrtl.strictconnect"(%0#1, %arg1) : (!firrtl.uint<1>, !firrtl.uint<1>) -> ()
  "firrtl.instance"() {annotations = [], inner_sym = #firrtl<"innerSym@MyView_companion">, moduleName = @MyView_companion, name = "MyView_companion", nameKind = #firrtl<"name_kind droppable_name">, portAnnotations = [], portDirections = 0 : i0, portNames = []} : () -> ()
}) {annotations = [{circt.nonlocal = @nla_3, class = "sifive.enterprise.grandcentral.ViewAnnotation.parent", id = 2 : i64, name = "MyView", type = "parent"}, {circt.nonlocal = @nla_4, class = "sifive.enterprise.grandcentral.ViewAnnotation.parent", id = 0 : i64, name = "MyView", type = "parent"}], portAnnotations = [[], []], portDirections = 0 : i2, portNames = ["clock", "reset"], portSyms = ["", ""], portTypes = [!firrtl.clock, !firrtl.uint<1>], sym_name = "Foo", sym_visibility = "private"} : () -> ()
firrtl/Top.fir:49:5: note: parent is instantiated here
    inst foo2 of Foo_1
    ^
firrtl/Top.fir:46:5: note: parent is instantiated here
    inst foo1 of Foo
    ^
firrtl/Top.fir:1:1: error: 'firrtl.circuit' op contains a 'companion' with id '2', but does not contain a GrandCentral 'parent' with the same id
circuit Top :
^

If you run this without dedup (or with a NoDedupAnnotation) everything expectedly works. The fundamental problem is that GCT Views are encoding an XMR using annotations. After Dedup runs, it rightly keeps both of these annotations around. However, now you have a multiply instantiated parent module that is supposed to describe two XMRs. However, these XMRs "deduplicate" to a single XMR.

There are multiple approaches to fix this:

  1. Modify the GCT Views pass to recognize this pattern and generate a "minimal" XMR that combines all the deduplicated XMRs.
  2. Special case certain annotations in dedup to also deduplicate.
  3. Change LowerAnnotations to represent XMRs as "fake ports" in the design rooted at the lowest-common ancestor necessary to make the connection. Then dedup works correctly.

(1) is janky, but expedient. (2) is extremely janky. (3) is the only reasonable solution here.

seldridge avatar Jul 08 '22 23:07 seldridge