circt
circt copied to clipboard
[FIRRTL] GCT Views and Dedup
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:
- Modify the GCT Views pass to recognize this pattern and generate a "minimal" XMR that combines all the deduplicated XMRs.
- Special case certain annotations in dedup to also deduplicate.
- 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.