Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Connectable operators: :<=, :>=, :<>=, and :#= #2806

Merged
merged 97 commits into from
Nov 30, 2022
Merged

Conversation

azidar
Copy link
Contributor

@azidar azidar commented Oct 24, 2022

Background

In general, Chisel's connection operators are very broken; their semantics change depending on import Chisel._ vs import chisel3._. Even chisel3._'s semantics are not clean; you cannot reason about what will be connected to what unless you know whether the things you are connecting are Wires or IOs. Part of this stems from the fact that we got rid of relative-flippedness for declaring things, instead relying on using Input/Output.

This PR defines four new operators which have very clean semantics which depend solely on the hardware type of the things you are connecting. Using them, we plan to migrate all existing operators semantics to compositions of them, but for now we define them independently of Chisel/chisel3, and thus they have more verbose syntax to make them compatible with both imports.

As we continue, we may rename them etc., but their semantics are composable which is an important property. In fact, there are only actually two operators which, when composed in different ways, can implement the semantics of other kinds of operators: :<= and :>=. While these operators may not be the most common ones to use, understanding them will help users reason about how all the connection operators work.

I've written a lot of ScalaDoc and mdoc to describe their semantics.

TODO:

  • Clean up tests (moving examples to own section, looking through old commented out ones to see if there are any useful ones to preserve, deleting ignored ones)
  • Remove changes from := to :#= and changes to test, as migration flag is off
  • Add Scaladoc to WaivedData
  • Clean up random things (commented out printlns, etc.)

Current requested reviewer feedback

  • Scaladoc and mdoc explanations are clear

Contributor Checklist

  • Did you add Scaladoc to every public function/method?
  • Did you add at least one test demonstrating the PR?
  • Did you delete any extraneous printlns/debugging code?
  • Did you specify the type of improvement?
  • Did you add appropriate documentation in docs/src?
  • Did you state the API impact?
  • Did you specify the code generation impact?
  • Did you request a desired merge strategy?
  • Did you add text to be included in the Release Notes for this change?

Type of Improvement

  • new feature/API

API Impact

No impact on current code. Adds new operators and documentation.

Desired Merge Strategy

  • Squash: The PR will be squashed and merged (choose this if you have no preference.

Release Notes

  • Added new operators :<>=, :<=, :>=, :#=
  • Added new Scaladoc to :=, <>

Reviewer Checklist (only modified by reviewer)

  • Did you add the appropriate labels?
  • Did you mark the proper milestone (Bug fix: 3.4.x, [small] API extension: 3.5.x, API modification or big change: 3.6.0)?
  • Did you review?
  • Did you check whether all relevant Contributor checkboxes have been checked?
  • Did you do one of the following when ready to merge:
    • Squash: You/ the contributor Enable auto-merge (squash), clean up the commit message, and label with Please Merge.
    • Merge: Ensure that contributor has cleaned up their commit history, then merge with Create a merge commit.

azidar added 30 commits October 11, 2022 10:00
Added documentation to :=, <>
Renamed ir.BulkConnect to ir.PartialConnect
Cleaned up some internal functions
…t), added check on := to barf on mixed bundles
Added documentation to :=, <>
Renamed ir.BulkConnect to ir.PartialConnect
Cleaned up some internal functions
@@ -284,7 +285,7 @@ The port-direction computation always computes alignment relative to the compone

The connection-direction computation always computes alignment based on the explicit consumer/producer referenced for the connection. If one connects `incoming :<>= outgoing`, alignments are computed based on `incoming` and `outgoing`. If one connects `incoming.alignedChild :<>= outgoing.alignedChild`, then alignments are computed based on `incoming.alignedChild` and `outgoing.alignedChild` (and the alignment of `incoming` to `incoming.alignedChild` is irrelevant).

This means that users can try to assign to input ports of their module! If I write `x :<>= y`, and `x` is an input to the current module, then that is what the connection is trying to do. However, because input ports are not assignable from within the current module, Chisel will throw an error. This is the same error a user would get using a mono-directioned operator: `x := y` will throw the same error if `x` is an input to the current module. *Whether a component is assignable is irrelevant to the semantics of any connection operator assigning to it.*
This means that users can try to connect to input ports of their module! If I write `x :<>= y`, and `x` is an input to the current module, then that is what the connection is trying to do. However, because input ports are not connectable from within the current module, Chisel will throw an error. This is the same error a user would get using a mono-directioned operator: `x := y` will throw the same error if `x` is an input to the current module. *Whether a component is connectable is irrelevant to the semantics of any connection operator connecting to it.*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may have gone a bit far with the connectable adjective here -- does connectable mean driveable? Assignable? Because to me 'connect' is not a direction thing and this whole paragraph doesn't really make sense any more

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we should use different terminology here like "assignable", "drivable", or "writeable".

We probably need to do hash out all of our terminology and then audit our docs to make sure they're consistent.

Copy link
Contributor

@jackkoenig jackkoenig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fantastic work, great job Adam!

I've left a lot of comments (and might drive by some more tomorrow) that I hope you incorporate. I'm approving because I don't think any of them are blocking so I hope you incorporate my feedback and then merge 🙂

Comment on lines 531 to 538
getRecursiveFields.noPath(this).collect {
case d if d.direction != this.direction =>
Builder.error(s"$this cannot be used with := because submember $d has inverse orientation; use :#= instead")
}
getRecursiveFields.noPath(that).collect {
case d if d.direction != that.direction =>
Builder.error(s"$that cannot be used with := because submember $d has inverse orientation; use :#= instead")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these recursive checks necessary? Is it not the case that this.direction will be ActualDirection.BidirectionalDirection if there are fields with differing directions? Put another way, I'm pretty sure that this.direction is derived from the fields.

These checks concern me because they are fairly expensive and at bare minimum should use .lazyNoPath to avoid a bunch of Seq allocations and appending to Lists.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only run if you are migrating the mono connect, so I think its ok for performance reasons?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to rely on 'ActualDirection' for anything.. maybe I'm just paranoid..

core/src/main/scala/chisel3/Data.scala Outdated Show resolved Hide resolved
core/src/main/scala/chisel3/Data.scala Show resolved Hide resolved
core/src/main/scala/chisel3/Data.scala Outdated Show resolved Hide resolved
core/src/main/scala/chisel3/connectable/Alignment.scala Outdated Show resolved Hide resolved
docs/src/explanations/connectable.md Outdated Show resolved Hide resolved
@@ -284,7 +285,7 @@ The port-direction computation always computes alignment relative to the compone

The connection-direction computation always computes alignment based on the explicit consumer/producer referenced for the connection. If one connects `incoming :<>= outgoing`, alignments are computed based on `incoming` and `outgoing`. If one connects `incoming.alignedChild :<>= outgoing.alignedChild`, then alignments are computed based on `incoming.alignedChild` and `outgoing.alignedChild` (and the alignment of `incoming` to `incoming.alignedChild` is irrelevant).

This means that users can try to assign to input ports of their module! If I write `x :<>= y`, and `x` is an input to the current module, then that is what the connection is trying to do. However, because input ports are not assignable from within the current module, Chisel will throw an error. This is the same error a user would get using a mono-directioned operator: `x := y` will throw the same error if `x` is an input to the current module. *Whether a component is assignable is irrelevant to the semantics of any connection operator assigning to it.*
This means that users can try to connect to input ports of their module! If I write `x :<>= y`, and `x` is an input to the current module, then that is what the connection is trying to do. However, because input ports are not connectable from within the current module, Chisel will throw an error. This is the same error a user would get using a mono-directioned operator: `x := y` will throw the same error if `x` is an input to the current module. *Whether a component is connectable is irrelevant to the semantics of any connection operator connecting to it.*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we should use different terminology here like "assignable", "drivable", or "writeable".

We probably need to do hash out all of our terminology and then audit our docs to make sure they're consistent.

docs/src/explanations/connectable.md Outdated Show resolved Hide resolved
docs/src/explanations/connectable.md Outdated Show resolved Hide resolved
docs/src/explanations/connectable.md Outdated Show resolved Hide resolved
@jackkoenig
Copy link
Contributor

Note that I left so many comments, many are hidden.

@jackkoenig jackkoenig added this to the 3.6.0 milestone Nov 18, 2022
Comment on lines +399 to +402

class Example9 extends RawModule {
val abType = new Record with AutoCloneType { val elements = SeqMap("a" -> Bool(), "b" -> Flipped(Bool())) }
val bcType = new Record with AutoCloneType { val elements = SeqMap("b" -> Flipped(Bool()), "c" -> Bool()) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class Example9 extends RawModule {
val abType = new Record with AutoCloneType { val elements = SeqMap("a" -> Bool(), "b" -> Flipped(Bool())) }
val bcType = new Record with AutoCloneType { val elements = SeqMap("b" -> Flipped(Bool()), "c" -> Bool()) }
class Example9 extends RawModule {
val abType = new Record with AutoCloneType { val elements = SeqMap(
"a" -> Bool(),
"b" -> Flipped(Bool()
)) }
val bcType = new Record with AutoCloneType { val elements = SeqMap(
"b" -> Flipped(Bool()),
"c" -> Bool()
) }

perhaps?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or, save this for later -- did you know we can run scalafmt on our .md files too...

@azidar azidar enabled auto-merge (squash) November 29, 2022 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants