summaryrefslogtreecommitdiff
path: root/docs/src/explanations
diff options
context:
space:
mode:
authorAdam Izraelevitz2020-10-05 12:29:40 -0700
committerGitHub2020-10-05 19:29:40 +0000
commit03e6c2ba149ac611d1e2329c4502fed0ccea48fe (patch)
treeea95f3ee6c697d45c4cbc2bd60f86a301d3e9379 /docs/src/explanations
parentaa2c62248002de97b95523c08d7788e9715e1313 (diff)
Move more docs (#1601)
* Fix broken mdoc * Added test-chisel-docs to ci workflow * Add requirement on build-treadle job * Added forgotton colon * Forgot cd into chisel3 dir * moved three docs into explanations * Updated reference Co-authored-by: Schuyler Eldridge <schuyler.eldridge@gmail.com>
Diffstat (limited to 'docs/src/explanations')
-rw-r--r--docs/src/explanations/annotations.md141
-rw-r--r--docs/src/explanations/blackboxes.md147
-rw-r--r--docs/src/explanations/bundles-and-vecs.md215
3 files changed, 503 insertions, 0 deletions
diff --git a/docs/src/explanations/annotations.md b/docs/src/explanations/annotations.md
new file mode 100644
index 00000000..19d24605
--- /dev/null
+++ b/docs/src/explanations/annotations.md
@@ -0,0 +1,141 @@
+---
+layout: docs
+title: "Annotations"
+section: "chisel3"
+---
+
+`Annotation`s are metadata containers associated with zero or more "things" in a FIRRTL circuit.
+Commonly, `Annotation`s are used to communicate information from Chisel to a specific, custom FIRRTL `Transform`.
+In this way `Annotation`s can be viewed as the "arguments" that a specific `Transform` consumes.
+
+This article focuses on the approach to building a basic library that contains `Annotation`s and `Transform`s.
+
+### Imports
+We need a few basic imports to reference the components we need.
+
+```scala mdoc:silent
+import chisel3._
+import chisel3.experimental.{annotate, ChiselAnnotation, RunFirrtlTransform}
+import chisel3.internal.InstanceId
+
+import firrtl._
+import firrtl.annotations.{Annotation, SingleTargetAnnotation}
+import firrtl.annotations.{CircuitTarget, ModuleTarget, InstanceTarget, ReferenceTarget, Target}
+```
+
+### Define an `Annotation` and a `Transform`
+
+First, define an `Annotation` that contains a string associated with a `Target` thing in the Chisel circuit.
+This `InfoAnnotation` extends [`SingleTargetAnnotation`](https://www.chisel-lang.org/api/firrtl/1.2.0/firrtl/annotations/SingleTargetAnnotation.html), an `Annotation` associated with *one* thing in a FIRRTL circuit:
+
+```scala mdoc:silent
+/** An annotation that contains some string information */
+case class InfoAnnotation(target: Target, info: String) extends SingleTargetAnnotation[Target] {
+ def duplicate(newTarget: Target) = this.copy(target = newTarget)
+}
+```
+
+Second, define a `Transform` that consumes this `InfoAnnotation`.
+This `InfoTransform` simply reads all annotations, prints any `InfoAnnotation`s it finds, and removes them.
+
+```scala mdoc:invisible
+object Issue1228 {
+ /* Workaround for https://github.com/freechipsproject/firrtl/pull/1228 */
+ abstract class Transform extends firrtl.Transform {
+ override def name: String = this.getClass.getName
+ }
+}
+import Issue1228.Transform
+```
+
+```scala mdoc:silent
+/** A transform that reads InfoAnnotations and prints information about them */
+class InfoTransform() extends Transform with DependencyAPIMigration {
+
+ override def prerequisites = firrtl.stage.Forms.HighForm
+
+ override def execute(state: CircuitState): CircuitState = {
+ println("Starting transform 'IdentityTransform'")
+
+ val annotationsx = state.annotations.flatMap{
+ case InfoAnnotation(a: CircuitTarget, info) =>
+ println(s" - Circuit '${a.serialize}' annotated with '$info'")
+ None
+ case InfoAnnotation(a: ModuleTarget, info) =>
+ println(s" - Module '${a.serialize}' annotated with '$info'")
+ None
+ case InfoAnnotation(a: InstanceTarget, info) =>
+ println(s" - Instance '${a.serialize}' annotated with '$info'")
+ None
+ case InfoAnnotation(a: ReferenceTarget, info) =>
+ println(s" - Component '${a.serialize} annotated with '$info''")
+ None
+ case a =>
+ Some(a)
+ }
+
+ state.copy(annotations = annotationsx)
+ }
+}
+```
+
+> Note: `inputForm` and `outputForm` will be deprecated in favor of a new dependency API that allows transforms to specify their dependencies more specifically than with circuit forms.
+> Full backwards compatibility for `inputForm` and `outputForm` will be maintained, however.
+
+### Create a Chisel API/Annotator
+
+Now, define a Chisel API to annotate Chisel things with this `InfoAnnotation`.
+This is commonly referred to as an "annotator".
+
+Here, define an object, `InfoAnnotator` with a method `info` that generates `InfoAnnotation`s.
+This uses the `chisel3.experimental.annotate` passed an anonymous `ChiselAnnotation` object.
+The need for this `ChiselAnnotation` (which is different from an actual FIRRTL `Annotation`) is that no FIRRTL circuit exists at the time the `info` method is called.
+This is delaying the generation of the `InfoAnnotation` until the full circuit is available.
+
+This annotator also mixes in the `RunFirrtlTransform` trait (abstract in the `transformClass` method) because this annotator, whenever used, should result in the FIRRTL compiler running the custom `InfoTransform`.
+
+```scala mdoc:silent
+object InfoAnnotator {
+ def info(component: InstanceId, info: String): Unit = {
+ annotate(new ChiselAnnotation with RunFirrtlTransform {
+ def toFirrtl: Annotation = InfoAnnotation(component.toTarget, info)
+ def transformClass = classOf[InfoTransform]
+ })
+ }
+}
+```
+
+> Note: there are a number of different approaches to writing an annotator.
+> You could use a trait that you mix into a `Module`, an object (like is done above), or any other software approach.
+> The specific choice of how you implement this is up to you!
+
+### Using the Chisel API
+
+Now, we can use the method `InfoAnnotation.info` to create annotations that associate strings with specific things in a FIRRTL circuit.
+Below is a Chisel `Module`, `ModC`, where both the actual module is annotated as well as an output.
+
+```scala mdoc:silent
+class ModC(widthC: Int) extends Module {
+ val io = IO(new Bundle {
+ val in = Input(UInt(widthC.W))
+ val out = Output(UInt(widthC.W))
+ })
+ io.out := io.in
+
+ InfoAnnotator.info(this, s"ModC($widthC)")
+
+ InfoAnnotator.info(io.out, s"ModC(ignore param)")
+}
+```
+
+### Running the Compilation
+
+Compiling this circuit to Verilog will then result in the `InfoTransform` running and the added `println`s showing information about the components annotated.
+
+```scala mdoc
+import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}
+
+// This currently doesn't work because of mdoc limitations. However, it will work
+// in your normal Scala code.
+//(new ChiselStage).execute(Array.empty, Seq(ChiselGeneratorAnnotation(() => new ModC(4))))
+```
diff --git a/docs/src/explanations/blackboxes.md b/docs/src/explanations/blackboxes.md
new file mode 100644
index 00000000..7064c7bb
--- /dev/null
+++ b/docs/src/explanations/blackboxes.md
@@ -0,0 +1,147 @@
+---
+layout: docs
+title: "Blackboxes"
+section: "chisel3"
+---
+Chisel *BlackBoxes* are used to instantiate externally defined modules. This construct is useful
+for hardware constructs that cannot be described in Chisel and for connecting to FPGA or other IP not defined in Chisel.
+
+Modules defined as a `BlackBox` will be instantiated in the generated Verilog, but no code
+will be generated to define the behavior of module.
+
+Unlike Module, `BlackBox` has no implicit clock and reset.
+`BlackBox`'s clock and reset ports must be explicitly declared and connected to input signals.
+Ports declared in the IO Bundle will be generated with the requested name (ie. no preceding `io_`).
+
+### Parameterization
+
+Verilog parameters can be passed as an argument to the BlackBox constructor.
+
+For example, consider instantiating a Xilinx differential clock buffer (IBUFDS) in a Chisel design:
+
+```scala mdoc:silent
+import chisel3._
+import chisel3.util._
+import chisel3.experimental._ // To enable experimental features
+
+class IBUFDS extends BlackBox(Map("DIFF_TERM" -> "TRUE",
+ "IOSTANDARD" -> "DEFAULT")) {
+ val io = IO(new Bundle {
+ val O = Output(Clock())
+ val I = Input(Clock())
+ val IB = Input(Clock())
+ })
+}
+
+class Top extends Module {
+ val io = IO(new Bundle{})
+ val ibufds = Module(new IBUFDS)
+ // connecting one of IBUFDS's input clock ports to Top's clock signal
+ ibufds.io.I := clock
+}
+```
+
+In the Chisel-generated Verilog code, `IBUFDS` will be instantiated as:
+
+```verilog
+IBUFDS #(.DIFF_TERM("TRUE"), .IOSTANDARD("DEFAULT")) ibufds (
+ .IB(ibufds_IB),
+ .I(ibufds_I),
+ .O(ibufds_O)
+);
+```
+
+### Providing Implementations for Blackboxes
+Chisel provides the following ways of delivering the code underlying the blackbox. Consider the following blackbox that
+ adds two real numbers together. The numbers are represented in chisel3 as 64-bit unsigned integers.
+```scala mdoc:silent:reset
+import chisel3._
+class BlackBoxRealAdd extends BlackBox {
+ val io = IO(new Bundle() {
+ val in1 = Input(UInt(64.W))
+ val in2 = Input(UInt(64.W))
+ val out = Output(UInt(64.W))
+ })
+}
+```
+
+The implementation is described by the following verilog
+```verilog
+module BlackBoxRealAdd(
+ input [63:0] in1,
+ input [63:0] in2,
+ output reg [63:0] out
+);
+ always @* begin
+ out <= $realtobits($bitstoreal(in1) + $bitstoreal(in2));
+ end
+endmodule
+```
+
+### Blackboxes with Verilog in a Resource File
+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.md). 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
+```mdoc scala:silent:reset
+class BlackBoxRealAdd extends BlackBox with HasBlackBoxResource {
+ val io = IO(new Bundle() {
+ val in1 = Input(UInt(64.W))
+ val in2 = Input(UInt(64.W))
+ val out = Output(UInt(64.W))
+ })
+ setResource("/real_math.v")
+}
+```
+The verilog snippet above gets put into a resource file names ```real_math.v```. What is a resource file? It comes from
+ a java convention of keeping files in a project that are automatically included in library distributions. In a typical
+ chisel3 project, see [chisel-template](https://github.com/ucb-bar/chisel-template), this would be a directory in the
+ source hierarchy
+```
+src/main/resources/real_math.v
+```
+
+### Blackboxes with In-line Verilog
+It is also possible to place this verilog directly in the scala source. Instead of ```HasBlackBoxResource``` use
+ ```HasBlackBoxInline``` and instead of ```setResource``` use ```setInline```. The code will look like this.
+```scala mdoc:silent:reset
+import chisel3._
+import chisel3.util.HasBlackBoxInline
+class BlackBoxRealAdd extends BlackBox with HasBlackBoxInline {
+ val io = IO(new Bundle() {
+ val in1 = Input(UInt(64.W))
+ val in2 = Input(UInt(64.W))
+ val out = Output(UInt(64.W))
+ })
+ setInline("BlackBoxRealAdd.v",
+ """module BlackBoxRealAdd(
+ | input [15:0] in1,
+ | input [15:0] in2,
+ | output [15:0] out
+ |);
+ |always @* begin
+ | out <= $realtobits($bitstoreal(in1) + $bitstoreal(in2));
+ |end
+ |endmodule
+ """.stripMargin)
+}
+```
+This technique will copy the inline verilog into the target directory under the name ```BlackBoxRealAdd.v```
+
+### Under the Hood
+This mechanism of delivering verilog content to the testing backends is implemented via chisel/firrtl annotations. The
+two methods, inline and resource, are two kinds of annotations that are created via the ```setInline``` and
+```setResource``` methods calls. Those annotations are passed through to the chisel-testers which in turn passes them
+on to firrtl. The default firrtl verilog compilers have a pass that detects the annotations and moves the files or
+inline test into the build directory. For each unique file added, the transform adds a line to a file
+black_box_verilog_files.f, this file is added to the command line constructed for verilator or vcs to inform them where
+to look.
+The [dsptools project](https://github.com/ucb-bar/dsptools) is a good example of using this feature to build a real
+number simulation tester based on black boxes.
+
+### The interpreter
+
+***Note that the FIRRTL Interpreter is deprecated. Please use Treadle, the new Chisel/FIRRTL simulator***
+The [firrtl interpreter](https://github.com/ucb-bar/firrtl-interpreter) uses a separate system that allows users to
+construct scala implementations of the black boxes. The scala implementation code built into a BlackBoxFactory which is
+passed down to the interpreter by the execution harness. The interpreter is a scala simulation tester. Once again the
+dsptools project uses this mechanism and is a good place to look at it.
+> It is planned that the BlackBoxFactory will be replaced by integration with the annotation based blackbox methods
+>stuff soon.
diff --git a/docs/src/explanations/bundles-and-vecs.md b/docs/src/explanations/bundles-and-vecs.md
new file mode 100644
index 00000000..4b1eb196
--- /dev/null
+++ b/docs/src/explanations/bundles-and-vecs.md
@@ -0,0 +1,215 @@
+---
+layout: docs
+title: "Bundles and Vecs"
+section: "chisel3"
+---
+```
+
+```
+
+`Bundle` and `Vec` are classes that allow the user to expand the set of Chisel datatypes with aggregates of other types.
+
+Bundles group together several named fields of potentially different types into a coherent unit, much like a `struct` in
+C. Users define their own bundles by defining a class as a subclass of `Bundle`.
+```scala mdoc:silent
+import chisel3._
+class MyFloat extends Bundle {
+ val sign = Bool()
+ val exponent = UInt(8.W)
+ val significand = UInt(23.W)
+}
+
+class ModuleWithFloatWire extends RawModule {
+ val x = Wire(new MyFloat)
+ val xs = x.sign
+}
+```
+
+> Currently, there is no way to create a bundle literal like ```8.U``` for ```UInt```s. Therefore, in order to create
+>literals for bundles, we must declare a [[wire|Combinational-Circuits#wires]] of that bundle type, and then assign
+>values to it. We are working on a way to declare bundle literals without requiring the creation of a Wire node and
+>assigning to it.
+
+```scala mdoc:silent
+class ModuleWithFloatConstant extends RawModule {
+ // Floating point constant.
+ val floatConst = Wire(new MyFloat)
+ floatConst.sign := true.B
+ floatConst.exponent := 10.U
+ floatConst.significand := 128.U
+}
+```
+
+A Scala convention is to capitalize the name of new classes and we suggest you follow that convention in Chisel too.
+
+Vecs create an indexable vector of elements, and are constructed as follows:
+```scala mdoc:silent
+class ModuleWithVec extends RawModule {
+ // Vector of 5 23-bit signed integers.
+ val myVec = Wire(Vec(5, SInt(23.W)))
+
+ // Connect to one element of vector.
+ val reg3 = myVec(3)
+}
+```
+
+(Note that we specify the number followed by the type of the `Vec` elements. We also specifiy the width of the `SInt`)
+
+The set of primitive classes
+(`SInt`, `UInt`, and `Bool`) plus the aggregate
+classes (`Bundles` and `Vec`s) all inherit from a common
+superclass, `Data`. Every object that ultimately inherits from
+`Data` can be represented as a bit vector in a hardware design.
+
+Bundles and Vecs can be arbitrarily nested to build complex data
+structures:
+```scala mdoc:silent
+class BigBundle extends Bundle {
+ // Vector of 5 23-bit signed integers.
+ val myVec = Vec(5, SInt(23.W))
+ val flag = Bool()
+ // Previously defined bundle.
+ val f = new MyFloat
+}
+```
+
+Note that the builtin Chisel primitive and aggregate classes do not
+require the `new` when creating an instance, whereas new user
+datatypes will. A Scala `apply` constructor can be defined so
+that a user datatype also does not require `new`, as described in
+[Function Constructor](../wiki-deprecated/functional-module-creation.md).
+
+### Flipping Bundles
+
+The `Flipped()` function recursively flips all elements in a Bundle/Record. This is very useful for building bidirectional interfaces that connect to each other (e.g. `Decoupled`). See below for an example.
+
+```scala mdoc:silent
+class ABBundle extends Bundle {
+ val a = Input(Bool())
+ val b = Output(Bool())
+}
+class MyFlippedModule extends RawModule {
+ // Normal instantiation of the bundle
+ // 'a' is an Input and 'b' is an Output
+ val normalBundle = IO(new ABBundle)
+ normalBundle.b := normalBundle.a
+
+ // Flipped recursively flips the direction of all Bundle fields
+ // Now 'a' is an Output and 'b' is an Input
+ val flippedBundle = IO(Flipped(new ABBundle))
+ flippedBundle.a := flippedBundle.b
+}
+```
+
+This generates the following Verilog:
+
+```scala mdoc
+import chisel3.stage.ChiselStage
+
+println(ChiselStage.emitVerilog(new MyFlippedModule()))
+```
+
+### MixedVec
+
+(Chisel 3.2+)
+
+All elements of a `Vec` must be of the same type. If we want to create a Vec where the elements have different types, we
+can use a MixedVec:
+
+```scala mdoc:silent
+import chisel3.util.MixedVec
+class ModuleMixedVec extends Module {
+ val io = IO(new Bundle {
+ val x = Input(UInt(3.W))
+ val y = Input(UInt(10.W))
+ val vec = Output(MixedVec(UInt(3.W), UInt(10.W)))
+ })
+ io.vec(0) := io.x
+ io.vec(1) := io.y
+}
+```
+
+We can also programmatically create the types in a MixedVec:
+
+```scala mdoc:silent
+class ModuleProgrammaticMixedVec(x: Int, y: Int) extends Module {
+ val io = IO(new Bundle {
+ val vec = Input(MixedVec((x to y) map { i => UInt(i.W) }))
+ // ...
+ })
+ // ...rest of the module goes here...
+}
+```
+
+### A note on `cloneType`
+
+Since Chisel is built on top of Scala and the JVM, it needs to know how to construct copies of bundles for various
+purposes (creating wires, IOs, etc). If you have a parametrized bundle and Chisel can't automatically figure out how to
+clone your bundle, you will need to create a custom `cloneType` method in your bundle. Most of the time, this is as
+simple as `override def cloneType = (new YourBundleHere(...)).asInstanceOf[this.type]`.
+
+Note that in the vast majority of cases, **this is not required** as Chisel can figure out how to clone most bundles
+automatically.
+
+Here is an example of a parametrized bundle (`ExampleBundle`) that features a custom `cloneType`.
+```scala mdoc:silent
+class ExampleBundle(a: Int, b: Int) extends Bundle {
+ val foo = UInt(a.W)
+ val bar = UInt(b.W)
+ override def cloneType = (new ExampleBundle(a, b)).asInstanceOf[this.type]
+}
+
+class ExampleBundleModule(btype: ExampleBundle) extends Module {
+ val io = IO(new Bundle {
+ val out = Output(UInt(32.W))
+ val b = Input(chiselTypeOf(btype))
+ })
+ io.out := io.b.foo + io.b.bar
+}
+
+class Top extends Module {
+ val io = IO(new Bundle {
+ val out = Output(UInt(32.W))
+ val in = Input(UInt(17.W))
+ })
+ val x = Wire(new ExampleBundle(31, 17))
+ x := DontCare
+ val m = Module(new ExampleBundleModule(x))
+ m.io.b.foo := io.in
+ m.io.b.bar := io.in
+ io.out := m.io.out
+}
+```
+
+Generally cloneType can be automatically defined if all arguments to the Bundle are vals e.g.
+
+```scala mdoc:silent
+class MyCloneTypeBundle(val bitwidth: Int) extends Bundle {
+ val field = UInt(bitwidth.W)
+ // ...
+}
+```
+
+The only caveat is if you are passing something of type Data as a "generator" parameter, in which case you should make
+it a `private val`.
+
+For example, consider the following Bundle. Because its `gen` variable is not a `private val`, the user has to
+explicitly define the `cloneType` method.
+
+```scala mdoc:silent
+import chisel3.util.{Decoupled, Irrevocable}
+class RegisterWriteIOExplicitCloneType[T <: Data](gen: T) extends Bundle {
+ val request = Flipped(Decoupled(gen))
+ val response = Irrevocable(Bool())
+ override def cloneType = new RegisterWriteIOExplicitCloneType(gen).asInstanceOf[this.type]
+}
+```
+
+We can make this this infer cloneType by making `gen` private since it is a "type parameter":
+
+```scala mdoc:silent
+class RegisterWriteIO[T <: Data](private val gen: T) extends Bundle {
+ val request = Flipped(Decoupled(gen))
+ val response = Irrevocable(Bool())
+}
+```