Skip to content

Commit

Permalink
Documentation and minor plugin changes. (#1573)
Browse files Browse the repository at this point in the history
* Added documentation. Bugfix in plugin. Moved plugin APIs to separate package

* Revert reg naming behavior (omit underscore)

* Added documentation and a test

* Addressed reviewer feedback.
  • Loading branch information
azidar authored Sep 14, 2020
1 parent 3b5fda0 commit 69e27b2
Show file tree
Hide file tree
Showing 9 changed files with 343 additions and 20 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ lazy val docs = project // new documentation project
.in(file("docs-target")) // important: it must not be docs/
.dependsOn(chisel)
.enablePlugins(MdocPlugin)
.settings(usePluginSettings: _*)
.settings(commonSettings)
.settings(
scalacOptions += "-language:reflectiveCalls",
Expand Down
9 changes: 0 additions & 9 deletions core/src/main/scala/chisel3/experimental/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,4 @@ package object experimental {
val prefix = chisel3.internal.prefix
// Use to remove prefixes not in provided scope
val noPrefix = chisel3.internal.noPrefix
// Used by Chisel's compiler plugin to automatically name signals
def autoNameRecursively[T <: Any](name: String, nameMe: T): T = {
chisel3.internal.Builder.nameRecursively(
name.replace(" ", ""),
nameMe,
(id: chisel3.internal.HasId, n: String) => id.autoSeed(n)
)
nameMe
}
}
2 changes: 1 addition & 1 deletion core/src/main/scala/chisel3/internal/Builder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ private[chisel3] trait HasId extends InstanceId {
* @param seed Seed for the name of this component
* @return this object
*/
def autoSeed(seed: String): this.type = {
private [chisel3] def autoSeed(seed: String): this.type = {
auto_seed = Some(seed)
for(hook <- auto_postseed_hooks) { hook(seed) }
prefix_seed = Builder.getPrefix()
Expand Down
22 changes: 22 additions & 0 deletions core/src/main/scala/chisel3/internal/plugin/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// See LICENSE for license details.

package chisel3.internal

package object plugin {
/** Used by Chisel's compiler plugin to automatically name signals
* DO NOT USE in your normal Chisel code!!!
*
* @param name The name to use
* @param nameMe The thing to be named
* @tparam T The type of the thing to be named
* @return The thing, but now named
*/
def autoNameRecursively[T <: Any](name: String, nameMe: T): T = {
chisel3.internal.Builder.nameRecursively(
name.replace(" ", ""),
nameMe,
(id: chisel3.internal.HasId, n: String) => id.autoSeed(n)
)
nameMe
}
}
62 changes: 62 additions & 0 deletions docs/src/cookbooks/naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
layout: docs
title: "Naming Cookbook"
section: "chisel3"
---

```scala mdoc:invisible
import chisel3.internal.plugin._
import chisel3._
import chisel3.experimental.prefix
import chisel3.experimental.noPrefix
import chisel3.stage.ChiselStage
```

### I still have _T signals, can this be fixed?

First check - is the compiler plugin properly enabled? Scalac plugins are enabled via the scalac option
`-Xplugin:<path/to/jar>`. You can check which compiler plugins are enabled by running `show Compile / scalacOptions` in
the sbt prompt.

If the plugin is enabled, these signals could be intermediate values which are consumed by either assertions or when
predicates. In these cases, the compiler plugin often can't find a good prefix for the generated intermediate signals.
We recommend you manually insert calls to `prefix` to fix these cases. We did this to Rocket Chip and saw huge benefits!

### I still see _GEN signals, can this be fixed?

`_GEN` signals are usually generated from the FIRRTL compiler, rather than the Chisel library. We are working on
renaming these signals with more context-dependent names, but it is a work in progress. Thanks for caring!

### My module names are super unstable - I change one thing and Queue_1 becomes Queue_42. Help!

This is the infamous `Queue` instability problem. In general, these cases are best solved at the source - the module
itself! If you overwrite `desiredName` to include parameter information (see the
[explanation](../explanations/naming.md#set-a-module-name) for more info), then this can avoid this problem permanantly.
We've done this with some Chisel utilities with great results!

### I want to add some hardware or assertions, but each time I do all the signal names get bumped!

This is the classic "ECO" problem, and we provide descriptions in [explanation](../explanations/naming.md). In short,
we recommend wrapping all additional logic in a prefix scope, which enables a unique namespace. This should prevent
name collisions, which are what triggers all those annoying signal name bumps!

### I want to force a signal (or instance) name to something, how do I do that?

Use the `.suggestName` method, which is on all classes which subtype 'Data'.

### All this prefixing is annoying, how do I fix it?

You can use the `noPrefix { ... }` to strip the prefix from all signals generated in that scope.

```scala mdoc
class ExampleNoPrefix extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = noPrefix { in + in + in }

out := add
}

println(ChiselStage.emitVerilog(new Example7))
```
229 changes: 229 additions & 0 deletions docs/src/explanations/naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
---
layout: docs
title: "Naming"
section: "chisel3"
---

Historically, Chisel has had trouble reliably capturing the names of signals. The reasons for this are due to (1)
primarily relying on reflection to find names, (2) using `@chiselName` macro which had unreliable behavior.

Chisel 3.4 introduced a custom Scala compiler plugin which enables reliabe and automatic capturing of signal names, when
they are declared. In addition, this release includes prolific use of a new prefixing API which enables more stable
naming of signals programmatically generated from function calls.

This document explains how naming now works in Chisel for signal and module names. For cookbook examples on how to fix
systemic name-stability issues, please refer to the naming [cookbook](../cookbooks/naming.md).

### Compiler Plugin

```scala mdoc
import chisel3.internal.plugin._
import chisel3._
import chisel3.experimental.prefix
import chisel3.experimental.noPrefix
import chisel3.stage.ChiselStage
```

With the release of Chisel 3.4, users should add the following line to their build.sbt settings to get the improved
naming:

```scala
addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % chiselVersion cross CrossVersion.full)
```

This plugin will run after the 'typer' phase of the Scala compiler. It looks for any user code which is of the form
`val x = y`, where `x` is of type `chisel3.Data`, `chisel3.MemBase`, or `chisel3.experimental.BaseModule`. For each
line which fits this criteria, it rewrites that line. In the following examples, the commented line is the what the
line above is rewritten to.

If the line is within a bundle declaration or is a module instantiation, it is rewritten to replace the right hand
side with a call to `autoNameRecursively`, which names the signal/module.

```scala mdoc
class MyBundle extends Bundle {
val foo = Input(UInt(3.W))
// val foo = autoNameRecursively("foo", Input(UInt(3.W)))
}
class Example1 extends MultiIOModule {
val io = IO(new MyBundle())
// val io = autoNameRecursively("io", IO(new MyBundle()))
}
println(ChiselStage.emitVerilog(new Example1))
```

Otherwise, it is rewritten to also include the name as a prefix to any signals generated while executing the right-hand-
side of the val declaration:

```scala mdoc
class Example2 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
// val in = autoNameRecursively("in", prefix("in")(IO(Input(UInt(2.W)))))

val out = IO(Output(UInt(2.W)))
// val out = autoNameRecursively("out", prefix("out")(IO(Output(UInt(2.W)))))

def inXin() = in * in

val add = 3.U + inXin()
// val add = autoNameRecursively("add", prefix("add")(3.U + inXin()))
// Note that the intermediate result of the multiplication is prefixed with `add`

out := add + 1.U
}

println(ChiselStage.emitVerilog(new Example2))
```

Note that the naming also works if the hardware type is nested in an `Option` or a subtype of `Iterable`:

```scala mdoc
class Example3 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
// val in = autoNameRecursively("in", prefix("in")(IO(Input(UInt(2.W)))))

val out = IO(Output(UInt()))
// val out = autoNameRecursively("out", prefix("out")(IO(Output(UInt(2.W)))))

def inXin() = in * in

val opt = Some(3.U + inXin())
// Note that the intermediate result of the inXin() is prefixed with `opt`:
// val opt = autoNameRecursively("opt", prefix("opt")(Some(3.U + inXin())))

out := opt.get + 1.U
}

println(ChiselStage.emitVerilog(new Example3))
```

### Prefixing

As shown above, the compiler plugin automatically attempts to prefix some of your signals for you. However, you as a
user can also add your own prefixes. This is especially for ECO-type fixes where you need to add some logic to a module
but don't want to influence other names in the module.

In the following example, we prefix additional logic with "ECO", where `Example4` is pre-ECO and `Example5` is post-ECO:

```scala mdoc
class Example4 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = in + in + in

out := add + 1.U
}

class Example5 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = in + in + in

out := prefix("ECO") { add + 1.U + in }
}

println(ChiselStage.emitVerilog(new Example4))
println(ChiselStage.emitVerilog(new Example5))

```

Also note that the prefixes append to each other (including the prefix generated by the compiler plugin):

```scala mdoc
class Example6 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = prefix("foo") { in + in + in }

out := add
}

println(ChiselStage.emitVerilog(new Example6))
```

Sometimes you may want to disable the prefixing. This might occur if you are writing a library function and
don't want the prefixing behavior. In this case, you can use the `noPrefix` object:

```scala mdoc
class Example7 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = noPrefix { in + in + in }

out := add
}

println(ChiselStage.emitVerilog(new Example7))
```

### Suggest a Signal's Name (or the instance name of a Module)

If you want to specify the name of a signal, you can always use the `.suggestName` API. Please note that the suggested
name will still be prefixed (including by the plugin). You can always use the `noPrefix` object to strip this.

```scala mdoc
class Example8 extends MultiIOModule {
val in = IO(Input(UInt(2.W)))
val out = IO(Output(UInt()))

val add = (in + (in + in).suggestName("foo"))

out := add
}

println(ChiselStage.emitVerilog(new Example8))
```

### Set a Module Name

If you want to specify the module's name (not the instance name of a module), you can always override the `desiredName`
value. Note that you can parameterize the name by the module's parameters. This is an excellent way to make your module
names more stable and is highly recommended to do.

```scala mdoc
class Example9(width: Int) extends MultiIOModule {
override val desiredName = s"EXAMPLE9WITHWIDTH$width"
val in = IO(Input(UInt(width.W)))
val out = IO(Output(UInt()))

val add = (in + (in + in).suggestName("foo"))

out := add
}

println(ChiselStage.emitVerilog(new Example9(8)))
println(ChiselStage.emitVerilog(new Example9(1)))
```

### Reflection Naming

Regardless of whether the compiler plugin is enabled or not, after Chisel constructs a module, it attempts to name all
members of the Module. This will name all vals which are fields of the module class, but it will not name any
vals in nested functions or scopes.

If the plugin successfully names a signal, the reflection naming will do nothing. We plan to deprecate all reflection
naming in a future Chisel release, but are leaving it to allow the plugin naming to be optional (but recommended).

For example, the signals in the following module are in a nested scope; the plugin successfully names them, but
reflection naming cannot:

```scala mdoc
class Example10 extends MultiIOModule {
{
val in = IO(Input(UInt(width.W)))
val out = IO(Output(UInt()))

val add = in + in

out := add
}
}
```

### @chiselName

This macro is no longer recommended as its functionality is entirely replaced by the compiler plugin. Feel free to
delete from your Chisel designs!
2 changes: 1 addition & 1 deletion plugin/src/main/resources/scalac-plugin.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<plugin>
<name>chiselplugin</name>
<classname>chisel3.plugin.ChiselPlugin</classname>
<classname>chisel3.internal.plugin.ChiselPlugin</classname>
</plugin>
Loading

0 comments on commit 69e27b2

Please sign in to comment.