Lowering to FIRRTL dialect

Hello everyone. I’m having trouble with lowering my dialect to FIRRTL dialect. I have a set of operations which directly corresponds to InstanceOps. Also I have a set of operations, which directly corresponds to ConnectOps. As far as I understand one should get the results from two InstanceOps (Value) and connect these results. However, as far as I can see, there is no string names in Value - you can only access them by index. Am I missing something? How should I connect two instances?

You are correct. FIRRTL Dialect instances return one result per port. A connection between two instances is then a connection between the results. You would normally create a connection in the compiler by doing something like builder.create<StrictConnectOp>(foo.getResult(0), bar.getResult(1)). You can access the port name array with the getPortNames* functions.

Port names are stored as an array attribute on both the instance and the module definition. This is used during custom printing of the instances and modules to give the results better names. You could search through this to find the port that has a specific name. However, you’d probably not normally need to do this.

Consider the following input FIRRTL text:

FIRRTL version 3.0.0
circuit Foo:
  module Bar:
    input a: UInt<1>
    output b: UInt<1>

    connect b, a

  module Baz:
    input a: UInt<1>
    output b: UInt<1>

    connect b, a

  module Foo:
    input a: UInt<1>
    output b: UInt<1>

    inst bar of Bar
    inst baz of Baz

    connect bar.a, a
    connect baz.a, bar.b
    connect b, baz.b

This is parsed into the following using firtool -parse-only and see that the connection is between the results of the instances:

module {
  firrtl.circuit "Foo" {
    firrtl.module private @Bar(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) {
      firrtl.strictconnect %b, %a : !firrtl.uint<1>
    }
    firrtl.module private @Baz(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) {
      firrtl.strictconnect %b, %a : !firrtl.uint<1>
    }
    firrtl.module @Foo(in %a: !firrtl.uint<1>, out %b: !firrtl.uint<1>) attributes {convention = #firrtl<convention scalarized>} {
      %bar_a, %bar_b = firrtl.instance bar interesting_name @Bar(in a: !firrtl.uint<1>, out b: !firrtl.uint<1>)
      %baz_a, %baz_b = firrtl.instance baz interesting_name @Baz(in a: !firrtl.uint<1>, out b: !firrtl.uint<1>)
      firrtl.strictconnect %bar_a, %a : !firrtl.uint<1>
      firrtl.strictconnect %baz_a, %bar_b : !firrtl.uint<1>
      firrtl.strictconnect %b, %baz_b : !firrtl.uint<1>
    }
  }
}

If you print this using the generic MLIR op printer (firtool -parse-only -mlir-print-op-generic), it shows more of what is going on under the hood. The connection is truly between %1#0 and %0#1:

%0:2 = "firrtl.instance"() {
  annotations = [],
  moduleName = @Bar,
  name = "bar",
  nameKind = #firrtl<name_kind interesting_name>,
  portAnnotations = [[], []],
  portDirections = -2 : i2,
  portNames = ["a", "b"]
} : () -> (!firrtl.uint<1>, !firrtl.uint<1>)
%1:2 = "firrtl.instance"() {
  annotations = [],
  moduleName = @Baz,
  name = "baz",
  nameKind = #firrtl<name_kind interesting_name>,
  portAnnotations = [[], []],
  portDirections = -2 : i2,
  portNames = ["a", "b"]
} : () -> (!firrtl.uint<1>, !firrtl.uint<1>)
"firrtl.strictconnect"(%0#0, %arg0) : (!firrtl.uint<1>, !firrtl.uint<1>) -> ()
"firrtl.strictconnect"(%1#0, %0#1) : (!firrtl.uint<1>, !firrtl.uint<1>) -> ()
"firrtl.strictconnect"(%arg1, %1#1) : (!firrtl.uint<1>, !firrtl.uint<1>) -> ()
1 Like

Thank you for your reply.
The problem is that I have an analogue of connectOp, which connects two instances by the names of their ports. Therefore, when I am lowering this operation to connectOp it would be more convenient to connect by names, not by indices.