-
Notifications
You must be signed in to change notification settings - Fork 18
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
Create chisel3-vs-compat.md #48
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,203 @@ | ||||||
--- | ||||||
layout: docs | ||||||
title: "chisel3 vs. compatibility mode" | ||||||
section: "chisel3" | ||||||
--- | ||||||
|
||||||
Chisel 3 supports "compatibility mode" which serves the purpose of bridging | ||||||
the semantic gap between Chisel 2 and Chisel 3 with Chisel2-like semantics. | ||||||
Chisel code using this mode depends on Chisel 3, yet the Scala code has | ||||||
`import Chisel._` instead of the normal `import chisel3._`. This page serves | ||||||
to document the differences for helping migrate from compatibility mode to the | ||||||
safer and more modern Chisel 3 semantics. | ||||||
|
||||||
### Connections | ||||||
|
||||||
#### Compatibility mode | ||||||
|
||||||
In compatibility mode, both connection operators `:=` and `<>` are actually the same. | ||||||
They both are bidirectional and noncommutative--the right side will be treated as | ||||||
the source and the left side will be treated as the sink. | ||||||
|
||||||
```scala | ||||||
import Chisel._ | ||||||
class MyModule extends Module { | ||||||
val io = new Bundle { | ||||||
val in = Decoupled(UInt(width = 4)).flip | ||||||
val out = Decoupled(UInt(width = 4)) | ||||||
} | ||||||
io.out <> io.in // This works | ||||||
io.out := io.in // This is equivalent | ||||||
io.in <> io.out // This will error | ||||||
} | ||||||
``` | ||||||
For Bundles, elements with matching names will be connected and unmatching elements | ||||||
will be ignored. For example: | ||||||
```scala | ||||||
import Chisel._ | ||||||
class BundleA extends Bundle { | ||||||
val foo = UInt(width = 8) | ||||||
} | ||||||
class BundleB extends Bundle { | ||||||
val foo = UInt(width = 8) | ||||||
val bar = UInt(width = 8) | ||||||
} | ||||||
class MyModule extends Module { | ||||||
val io = new Bundle { | ||||||
val in = (new BundleA).asInput | ||||||
val out = (new BundleB).asOutput | ||||||
} | ||||||
io.out <> io.in | ||||||
// Equivalent to | ||||||
io.out.foo := io.in.foo | ||||||
// bar is ignored because it doesn't match | ||||||
} | ||||||
``` | ||||||
|
||||||
Furthermore, error detection and reporting is defered to FIRRTL compilation. | ||||||
|
||||||
#### Chisel 3 | ||||||
|
||||||
In Chisel 3, `:=` is refered to as "monoconnect" and `<>` is called "biconnect". | ||||||
|
||||||
* Monoconnect | ||||||
`:=` treats everything on the left-hand side as a sink, even if the type is | ||||||
bidirectional. This means it cannot be used to drive bidirectional output ports, | ||||||
but can be used to drive wires from bidirectional inputs or outputs to "monitor" | ||||||
the the full aggregate, ignoring directions. | ||||||
|
||||||
* Biconnect | ||||||
`<>` performs bidirectional connections and is commutative. At least one of the | ||||||
arguments must be a port. | ||||||
It will determine the correct leaf-level connections based on the directions of | ||||||
its port arguments. | ||||||
It cannot be used to connect two components _inside_ of a module. | ||||||
|
||||||
```scala | ||||||
import chisel3._ | ||||||
import chisel3.util._ | ||||||
class MyModule extends Module { | ||||||
val io = IO(new Bundle { | ||||||
val in = Flipped(Decoupled(UInt(4.W))) | ||||||
val out = Decoupled(UInt(4.W)) | ||||||
val x = Output(UInt(8.W)) | ||||||
}) | ||||||
io.out <> io.in // This works | ||||||
io.in <> io.out // So does this | ||||||
io.out := io.in // Error, cannot drive io.out.ready | ||||||
|
||||||
val w = Wire(Decoupled(UInt(4.W))) | ||||||
w := io.in // This works, w can be seen as "monitoring" io.in | ||||||
4.U <> io.x // This also works but is stylistically suspect | ||||||
} | ||||||
``` | ||||||
In contrast to compatibility mode, every field of two connected Bundles must match. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this only true for |
||||||
For example: | ||||||
```scala | ||||||
import chisel3._ | ||||||
class BundleA extends Bundle { | ||||||
val foo = UInt(8.W) | ||||||
} | ||||||
class BundleB extends Bundle { | ||||||
val foo = UInt(8.W) | ||||||
val bar = UInt(8.W) | ||||||
} | ||||||
class MyModule extends Module { | ||||||
val io = new Bundle { | ||||||
val in = Input(new BundleA) | ||||||
val out = Output(new BundleB) | ||||||
} | ||||||
io.out <> io.in // This is an error because io.in.bar doesn't match | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So how to do this properly assuming the user wants the same code to be generated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a method defined on data so that wouldn't work, but you could define a new operator via an implicit conversion: object CompatConnect {
import Chisel._
implicit class sketchyBulkConnectable(data: Data) {
def <*>(that: Data): Unit = data <> that
}
} You can then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fundamentally, relying on this behavior was considered dangerous because in refactoring code if you added a field it would bulk connect anyway and lead to subtle bugs. I think there is a very strong argument that such behavior should work when used with inheritance, eg. if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that this is the desired/intended behavior in most extant use cases |
||||||
} | ||||||
``` | ||||||
Additionally, errors are caught during Chisel elaboration. | ||||||
|
||||||
### Width Declaration | ||||||
|
||||||
#### Compatibility mode | ||||||
```scala | ||||||
val x = UInt(width = 8) | ||||||
``` | ||||||
#### Chisel 3 | ||||||
```scala | ||||||
val x = UInt(8.W) | ||||||
``` | ||||||
Notably, widths are now a type rather than a byname argument to the `UInt` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I just realized that what I really mean is "named argument", by-name is when you put |
||||||
constructor. | ||||||
|
||||||
|
||||||
### Literals | ||||||
|
||||||
#### Compatibility mode | ||||||
```scala | ||||||
val x = UInt(2) | ||||||
val y = SInt(-1, width = 8) | ||||||
``` | ||||||
#### Chisel 3 | ||||||
```scala | ||||||
val x = 2.U | ||||||
val y = -1.S(8.W) | ||||||
``` | ||||||
|
||||||
### Direction | ||||||
|
||||||
#### Compatibility mode | ||||||
```scala | ||||||
class MyBundle { | ||||||
val foo = UInt(8.W).asInput | ||||||
val bar = UInt(8.W) // Default direction is Output | ||||||
} | ||||||
val a = UInt(INPUT, 8) | ||||||
val b = Bool(OUTPUT) | ||||||
val c = Bool() // Equivalent to above, default direction is OUTPUT | ||||||
val d = new MyBundle | ||||||
val e = (new Bundle).flip | ||||||
``` | ||||||
#### Chisel 3 | ||||||
```scala | ||||||
// Directions are required | ||||||
class MyBundle { | ||||||
val foo = Input(UInt(8.W)) | ||||||
val bar = Output(UInt(8.W)) | ||||||
} | ||||||
val a = Input(UInt(8.W)) | ||||||
val b = Output(Bool()) | ||||||
val c = new MyBundle | ||||||
val d = Flipped(new MyBundle) | ||||||
``` | ||||||
|
||||||
### Module IO | ||||||
#### Compatibility mode | ||||||
```scala | ||||||
class MyModule extends Module { | ||||||
val io = new Bundle { ... } | ||||||
} | ||||||
``` | ||||||
#### Chisel 3 | ||||||
|
||||||
In Chisel 3, all declared ports must be wrapped in `IO(...)`. For `Modules`, | ||||||
this is just `val io`. | ||||||
```scala | ||||||
class MyModule extends Module { | ||||||
val io = IO(new Bundle { ... } ) | ||||||
} | ||||||
``` | ||||||
But in Chisel 3, `MultiIOModules` have the ability to declare multiple "ios". | ||||||
While `val io` is not required, the implicit `clock` and `reset` are still there. | ||||||
```scala | ||||||
class MyModule2 extends MultiIOModule { | ||||||
val foo = IO(Input(UInt(8.W))) | ||||||
} | ||||||
``` | ||||||
Chisel 3 also has `RawModule` which allows arbitrary ports and has no implicit | ||||||
`clock` nor `reset`. | ||||||
```scala | ||||||
class MyModule3 extends RawModule { | ||||||
val foo = IO(Output(Vec(8, Bool()))) | ||||||
} | ||||||
``` | ||||||
|
||||||
### Utilities | ||||||
|
||||||
Many constructs originally available via `import Chisel._` are now located in | ||||||
`chisel3.util`. They can be imported | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there text missing? They can be imported doesn't convey much information. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah sorry, this is a WIP and I jumped around, sorry I should've made this a draft PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So.... how are you supposed to do that? Do you have to use two monoconnects?