Skip to content

Commit 3355f26

Browse files
authored
[core] Deprecate BlackBox in favor of ExtModule (#5103)
Deprecate BlackBox and tell users to switch to ExtModule. BlackBox has a ton of baggage associated with it, due to it mandating, at runtime, a single `val io` call and no additional calls to `IO` while also magically removing the `io_` prefix in the generated FIRRTL. `ExtModule` is far cleaner and works as a user would expect. You can call `IO` as much as you want. No magic prefixing happens. This is slightly unfortunate as one could argue that `BlackBox` is a good name. However, you can also argue (more strongly I would add) that this is an "external module" and it would be better to call it as such. If this is controversial, we can consider a migration back from `ExtModule` to `BlackBox`. Migrate some tests that were only testing blackboxes to be duplicated in the external module tests. This will allow us to just delete this test later. This has three motivations: 1. `BlackBox` has always been janky for the reasons above and `ExtModule` is the saner generator API. 2. It's bad to have two similar, though subtly different ways of doing things. 3. The singular IO that `BlackBox` forces users to go through means that `BlackBox` cannot have domain information attached to it. This essentially means that `BlackBox` will be essentially unusable until domains gain support for being in subfields. It's easier to just deprecate `BlackBox` than to require that domains gain this complicated feature addition just now. Signed-off-by: Schuyler Eldridge <[email protected]>
1 parent bf4fa93 commit 3355f26

31 files changed

+327
-152
lines changed

core/src/main/scala/chisel3/BlackBox.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ import scala.collection.mutable
4646
* }}}
4747
* @note The parameters API is experimental and may change
4848
*/
49+
@deprecated(
50+
"use `chisel3.ExtModule` instead. To migrate replace `val io = { ... }` with `val io = FlatIO { ... }`, `object io { ... }; locally { io }`, or manually flatten your IO as `ExtModule` allows calling `IO` more than once.",
51+
"7.5.0"
52+
)
4953
abstract class BlackBox(
5054
val params: Map[String, Param] = Map.empty[String, Param],
5155
override protected final val knownLayers: Seq[Layer] = Seq.empty[Layer]

core/src/main/scala/chisel3/Module.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
package chisel3
44

5+
import scala.annotation.nowarn
56
import scala.collection.immutable.{ListMap, VectorBuilder}
67
import scala.collection.mutable.{ArrayBuffer, HashMap, LinkedHashSet}
78

@@ -370,7 +371,7 @@ package internal {
370371
// currentModule (and not clonePorts)
371372
val clonePorts = proto match {
372373
// BlackBox needs special handling for its pseduo-io Bundle
373-
case b: BlackBox =>
374+
case b: BlackBox @nowarn("cat=deprecation") =>
374375
new ClonePorts(dataPorts :+ ("io" -> b._io.get): _*)
375376
case _ => new ClonePorts(dataPorts: _*)
376377
}

core/src/main/scala/chisel3/experimental/ExtModule.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,6 @@ private[chisel3] object BlackBoxHelpers {
4141
}
4242
import BlackBoxHelpers.BlackBoxInlineAnnoHelpers
4343

44-
private object ExtModule {
45-
final val deprecatedCaseClass =
46-
"this has moved from `chisel3.experimental` to `chisel3` and all `case class` methods are deprecated. This will be made a `class` in Chisel 8."
47-
final val since = "7.5.0"
48-
}
49-
import ExtModule._
50-
5144
/** Parameters for BlackBoxes */
5245
@deprecated(deprecatedCaseClass, since)
5346
abstract class Param

core/src/main/scala/chisel3/experimental/hierarchy/ModuleClone.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import chisel3.internal.firrtl.ir.{Component, ModuleCloneIO, Ref}
88
import chisel3.internal.{throwException, Namespace}
99
import chisel3.layer.Layer
1010
import chisel3._
11+
import scala.annotation.nowarn
1112

1213
// Private internal class to serve as a _parent for Data in cloned ports
1314
private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T)(implicit si: SourceInfo)
@@ -37,7 +38,7 @@ private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T)(implicit si
3738
private[chisel3] lazy val ioMap: Map[Data, Data] = {
3839
getProto match {
3940
// BlackBox needs special handling for its pseduo-io Bundle
40-
case protoBB: BlackBox =>
41+
case protoBB: BlackBox @nowarn("cat=deprecation") =>
4142
Map(protoBB._io.get -> getPorts._elements("io"))
4243
case _ =>
4344
val name2Port = getPorts._elements
@@ -64,7 +65,7 @@ private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T)(implicit si
6465
record.setRef(ref, force = true) // force because we did .forceName first
6566
getProto match {
6667
// BlackBox needs special handling for its pseduo-io Bundle
67-
case _: BlackBox =>
68+
case _: BlackBox @nowarn("cat=deprecation") =>
6869
// Override the io Bundle's ref so that it thinks it is the top for purposes of
6970
// generating FIRRTL
7071
record._elements("io").setRef(ref, force = true)

core/src/main/scala/chisel3/internal/BiConnect.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import chisel3.internal.Builder.pushCommand
1111
import chisel3.internal.firrtl.ir.{Connect, DefInvalid}
1212
import chisel3.internal.firrtl.Converter
1313
import chisel3.internal.MonoConnect.reportIfReadOnly
14+
import scala.annotation.nowarn
1415

1516
import _root_.firrtl.passes.CheckTypes
1617

@@ -309,8 +310,8 @@ private[chisel3] object BiConnect {
309310

310311
// do not bulk connect the 'io' pseudo-bundle of a BlackBox since it will be decomposed in FIRRTL
311312
def blackBoxCheck = Seq(source, sink).map(_._parent).forall {
312-
case Some(_: BlackBox) => false
313-
case _ => true
313+
case Some(_: BlackBox @nowarn("cat=deprecation")) => false
314+
case _ => true
314315
}
315316

316317
typeCheck && contextCheck && bindingCheck && flowSinkCheck && flowSourceCheck && sourceAndSinkNotLiteralOrViewCheck && blackBoxCheck

docs/src/appendix/experimental-features.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ Chisel has a number of new features that are worth checking out. This page is a
1616
The standard Chisel *Module* requires a `val io = IO(...)`, the experimental package introduces several
1717
new ways of defining Modules
1818
- BaseModule: no contents, instantiable
19-
- BlackBox extends BaseModule
2019
- UserDefinedModule extends BaseModule: this module can contain Chisel RTL. No default clock or reset lines. No default IO. - User should be able to specify non-io ports, ideally multiple of them.
2120
- ImplicitModule extends UserModule: has clock, reset, and io, essentially current Chisel Module.
2221
- RawModule: will be the user-facing version of UserDefinedModule

docs/src/cookbooks/cookbook.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -980,16 +980,14 @@ chisel3.docs.emitSystemVerilog(new Foo2)
980980

981981
### How can I dynamically set/parametrize the name of a module?
982982

983-
You can override the `desiredName` function. This works with normal Chisel modules and `BlackBox`es. Example:
983+
You can override the `desiredName` function. This works with normal Chisel modules and external modules. Example:
984984

985985
```scala mdoc:silent:reset
986986
import chisel3._
987987

988-
class Coffee extends BlackBox {
989-
val io = IO(new Bundle {
990-
val I = Input(UInt(32.W))
991-
val O = Output(UInt(32.W))
992-
})
988+
class Coffee extends ExtModule {
989+
val I = IO(Input(UInt(32.W)))
990+
val O = IO(Output(UInt(32.W)))
993991
override def desiredName = "Tea"
994992
}
995993

@@ -998,7 +996,7 @@ class Salt extends Module {
998996
val drink = Module(new Coffee)
999997
override def desiredName = "SodiumMonochloride"
1000998

1001-
drink.io.I := 42.U
999+
drink.I := 42.U
10021000
}
10031001
```
10041002

Lines changed: 41 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,44 @@
11
---
22
layout: docs
3-
title: "Blackboxes"
3+
title: |
4+
External Modules ("Black Boxes")
45
section: "chisel3"
56
---
67

7-
# BlackBoxes
8+
# External Modules ("Black Boxes")
89

9-
Chisel *BlackBoxes* are used to instantiate externally defined modules. This construct is useful
10+
Chisel *External Modules* are used to instantiate externally defined modules. This construct is useful
1011
for hardware constructs that cannot be described in Chisel and for connecting to FPGA or other IP not defined in Chisel.
1112

12-
Modules defined as a `BlackBox` will be instantiated in the generated Verilog, but no code
13+
Modules defined as an `ExtModule` will be instantiated in the generated Verilog, but no code
1314
will be generated to define the behavior of module.
1415

15-
Unlike Module, `BlackBox` has no implicit clock and reset.
16-
`BlackBox`'s clock and reset ports must be explicitly declared and connected to input signals.
17-
Ports declared in the IO Bundle will be generated with the requested name (ie. no preceding `io_`).
16+
Unlike Module, `ExtModule` has no implicit clock and reset.
17+
Instead, they behave like `RawModule` in this regard.
18+
`ExtModule`'s clock and reset ports must be explicitly declared and connected to input signals.
1819

1920
### Parameterization
2021

21-
Verilog parameters can be passed as an argument to the BlackBox constructor.
22+
Verilog parameters can be passed as an argument to the `ExtModule` constructor.
2223

2324
For example, consider instantiating a Xilinx differential clock buffer (IBUFDS) in a Chisel design:
2425

2526
```scala mdoc:silent
2627
import chisel3._
2728
import chisel3.util._
28-
import chisel3.experimental._ // To enable experimental features
29+
import chisel3.experimental.fromStringToStringParam
2930

30-
class IBUFDS extends BlackBox(Map("DIFF_TERM" -> "TRUE",
31+
class IBUFDS extends ExtModule(Map("DIFF_TERM" -> "TRUE",
3132
"IOSTANDARD" -> "DEFAULT")) {
32-
val io = IO(new Bundle {
33-
val O = Output(Clock())
34-
val I = Input(Clock())
35-
val IB = Input(Clock())
36-
})
33+
val O = IO(Output(Clock()))
34+
val I = IO(Input(Clock()))
35+
val IB = IO(Input(Clock()))
3736
}
3837

3938
class Top extends Module {
40-
val io = IO(new Bundle {})
4139
val ibufds = Module(new IBUFDS)
4240
// connecting one of IBUFDS's input clock ports to Top's clock signal
43-
ibufds.io.I := clock
41+
ibufds.I := clock
4442
}
4543
```
4644

@@ -54,26 +52,24 @@ IBUFDS #(.DIFF_TERM("TRUE"), .IOSTANDARD("DEFAULT")) ibufds (
5452
);
5553
```
5654

57-
### Providing Implementations for Blackboxes
55+
### Providing Implementations for External Modules
5856

59-
Chisel provides the following ways of delivering the code underlying the blackbox. Consider the following blackbox that
57+
Chisel provides the following ways of delivering the code underlying the external module. Consider the following external module that
6058
adds two real numbers together. The numbers are represented in chisel3 as 64-bit unsigned integers.
6159

6260
```scala mdoc:silent:reset
6361
import chisel3._
64-
class BlackBoxRealAdd extends BlackBox {
65-
val io = IO(new Bundle {
66-
val in1 = Input(UInt(64.W))
67-
val in2 = Input(UInt(64.W))
68-
val out = Output(UInt(64.W))
69-
})
62+
class ExtModuleRealAdd extends ExtModule {
63+
val in1 = IO(Input(UInt(64.W)))
64+
val in2 = IO(Input(UInt(64.W)))
65+
val out = IO(Output(UInt(64.W)))
7066
}
7167
```
7268

7369
The implementation is described by the following verilog
7470

7571
```verilog
76-
module BlackBoxRealAdd(
72+
module ExtModuleRealAdd(
7773
input [63:0] in1,
7874
input [63:0] in2,
7975
output reg [63:0] out
@@ -84,20 +80,17 @@ module BlackBoxRealAdd(
8480
endmodule
8581
```
8682

87-
### Blackboxes with Verilog in a Resource File
83+
### External Modules with Verilog in a Resource File
8884

89-
In order to deliver the verilog snippet above to the backend simulator, chisel3 provides the following tools based on the chisel/firrtl [annotation system](../explanations/annotations). Add the trait `HasBlackBoxResource` to the declaration, and then call a function in the body to say where the system can find the verilog. The Module now looks like
85+
In order to deliver the Verilog snippet above to the backend simulator, chisel3 provides the following tools basedf on the Chisel/FIRRTL [annotation system](../explanations/annotations) via methods that are already available on `ExtModule`. To include a Java resource, use `addResource`:
9086

9187
```scala mdoc:silent:reset
9288
import chisel3._
93-
import chisel3.util.HasBlackBoxResource
94-
95-
class BlackBoxRealAdd extends BlackBox with HasBlackBoxResource {
96-
val io = IO(new Bundle {
97-
val in1 = Input(UInt(64.W))
98-
val in2 = Input(UInt(64.W))
99-
val out = Output(UInt(64.W))
100-
})
89+
90+
class ExtModuleRealAdd extends ExtModule {
91+
val in1 = IO(Input(UInt(64.W)))
92+
val in2 = IO(Input(UInt(64.W)))
93+
val out = IO(Output(UInt(64.W)))
10194
addResource("/real_math.v")
10295
}
10396
```
@@ -107,21 +100,18 @@ The verilog snippet above gets put into a resource file names `real_math.v`. Wh
107100
Chisel project, see [chisel-template](https://github.com/chipsalliance/chisel-template), this would be a directory in the
108101
source hierarchy: `src/main/resources/real_math.v`.
109102

110-
### Blackboxes with In-line Verilog
111-
It is also possible to place this verilog directly in the scala source. Instead of `HasBlackBoxResource` use
112-
`HasBlackBoxInline` and instead of `setResource` use `setInline`. The code will look like this.
103+
### External Modules with In-line Verilog
104+
It is also possible to place this Verilog directly in the scala source. Instead
105+
of `addResource` use `setInline`. The code will look like this:
113106

114107
```scala mdoc:silent:reset
115108
import chisel3._
116-
import chisel3.util.HasBlackBoxInline
117-
class BlackBoxRealAdd extends BlackBox with HasBlackBoxInline {
118-
val io = IO(new Bundle {
119-
val in1 = Input(UInt(64.W))
120-
val in2 = Input(UInt(64.W))
121-
val out = Output(UInt(64.W))
122-
})
123-
setInline("BlackBoxRealAdd.v",
124-
"""module BlackBoxRealAdd(
109+
class ExtModuleRealAdd extends ExtModule {
110+
val in1 = IO(Input(UInt(64.W)))
111+
val in2 = IO(Input(UInt(64.W)))
112+
val out = IO(Output(UInt(64.W)))
113+
setInline("ExtModuleRealAdd.v",
114+
"""module ExtModuleRealAdd(
125115
| input [63:0] in1,
126116
| input [63:0] in2,
127117
| output reg [63:0] out
@@ -134,16 +124,15 @@ class BlackBoxRealAdd extends BlackBox with HasBlackBoxInline {
134124
}
135125
```
136126

137-
This technique will copy the inline verilog into the target directory under the name `BlackBoxRealAdd.v`
127+
This technique will copy the inline verilog into the target directory under the name `ExtModuleRealAdd.v`
138128

139129
### Under the Hood
140-
This mechanism of delivering verilog content to the testing backends is implemented via chisel/firrtl annotations. The
130+
This mechanism of delivering verilog content to the testing backends is implemented via Chisel/FIRRTL annotations. The
141131
two methods, inline and resource, are two kinds of annotations that are created via the `setInline` and
142-
`setResource` methods calls. Those annotations are passed through to the chisel-testers which in turn passes them
132+
`addResource` methods calls. Those annotations are passed through to the chisel-testers which in turn passes them
143133
on to firrtl. The default firrtl verilog compilers have a pass that detects the annotations and moves the files or
144134
inline test into the build directory. For each unique file added, the transform adds a line to a file
145135
`black_box_verilog_files.f`, this file is added to the command line constructed for verilator or vcs to inform them where
146136
to look.
147137
The [dsptools project](https://github.com/ucb-bar/dsptools) is a good example of using this feature to build a real
148138
number simulation tester based on black boxes.
149-

docs/src/explanations/dataview.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class my_module extends RawModule {
6060
```
6161

6262
Expressing something that matches a standard Verilog interface is important when instantiating Verilog
63-
modules in a Chisel design as `BlackBoxes`.
63+
modules in a Chisel design as `ExtModule`s.
6464
Generally though, Chisel developers prefer to use composition via utilities like `Decoupled` rather
6565
than a flat handling of `ready` and `valid` as in the above.
6666
A more "Chisel-y" implementation of this interface might look like:
@@ -266,7 +266,7 @@ import chisel3._
266266
import chisel3.experimental.dataview._
267267
```
268268

269-
A `DataView` is _total_ if all fields of the _Target_ type and all fields of the _View_ type are
269+
A `DataView` is _total_ if all fields of the _Target_ type and all fields of the _View_ type are
270270
included in the mapping.
271271
Chisel will error if a field is accidentally left out from a `DataView`.
272272
For example:
@@ -341,7 +341,7 @@ polymorphic code.
341341
They are a common feature in "modern programming languages" like
342342
Scala,
343343
Swift (see [protocols](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html)),
344-
and Rust (see [traits](https://doc.rust-lang.org/book/ch10-02-traits.html)).
344+
and Rust (see [traits](https://doc.rust-lang.org/book/ch10-02-traits.html)).
345345
Type classes may appear similar to inheritance in object-oriented programming but there are some
346346
important differences:
347347

@@ -377,7 +377,7 @@ before looking in the _implicit scope_.
377377
* Companion object of a type
378378
* Implicit scope of an argument's type
379379
* Implicit scope of type parameters
380-
380+
381381
If at either stage, multiple implicits are found, then the static overloading rule is used to resolve
382382
it.
383383
Put simply, if one implicit applies to a more-specific type than the other, the more-specific one
@@ -510,4 +510,3 @@ This is how Chisel is able to check for totality as [described above](#totality-
510510
In addition to checking if a user has left a field out of the mapping, it also allows Chisel to check
511511
if the user has included a `Data` in the mapping that isn't actually a part of the _target_ nor the
512512
_view_.
513-

docs/src/explanations/modules.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,4 @@ modules, then using `withClockAndReset(clock, !rstn)` we can use an active low
129129
reset in the entire design.
130130

131131
The clock is just wired as is, but if needed, `RawModule` can be used in
132-
conjunction with `BlackBox` to connect a differential clock input for example.
132+
conjunction with `ExtModule` to connect a differential clock input for example.

0 commit comments

Comments
 (0)