From f1ad5b58e8a749d558758288d03ce75bf6b8ff9c Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Thu, 18 Mar 2021 16:47:58 -0700 Subject: Reorganize website docs (#1806) Updates to chisel3 documentation for website: * guard code examples with mdoc and fix errors encountered along the way * move some website content here vs splitting the content across two repos * Bring in the interval-types and loading memories content so that it will be visible from the website * remove all references to the wiki (deprecated) * Remove reference to Wiki from the README * fix tabbing and compile of chisel3-vs-chisel2 section * Appendix: faqs now guarded and compile * FAQs: move to resources section--- docs/src/explanations/annotations.md | 8 +- docs/src/explanations/bundles-and-vecs.md | 2 +- docs/src/explanations/combinational-circuits.md | 84 ++++++++ docs/src/explanations/data-types.md | 136 ++++++++++++ docs/src/explanations/explanations.md | 39 ++++ docs/src/explanations/functional-abstraction.md | 34 +++ .../src/explanations/functional-module-creation.md | 120 +++++++++++ .../src/explanations/interfaces-and-connections.md | 149 +++++++++++++ docs/src/explanations/memories.md | 176 +++++++++++++++ docs/src/explanations/modules.md | 138 ++++++++++++ docs/src/explanations/motivation.md | 44 ++++ docs/src/explanations/muxes-and-input-selection.md | 62 ++++++ docs/src/explanations/operators.md | 65 ++++++ .../polymorphism-and-parameterization.md | 237 +++++++++++++++++++++ docs/src/explanations/ports.md | 67 ++++++ docs/src/explanations/printing.md | 137 ++++++++++++ docs/src/explanations/reset.md | 179 ++++++++++++++++ docs/src/explanations/sequential-circuits.md | 50 +++++ docs/src/explanations/supported-hardware.md | 11 + docs/src/explanations/unconnected-wires.md | 138 ++++++++++++ docs/src/explanations/width-inference.md | 40 ++++ 21 files changed, 1911 insertions(+), 5 deletions(-) create mode 100644 docs/src/explanations/combinational-circuits.md create mode 100644 docs/src/explanations/data-types.md create mode 100644 docs/src/explanations/explanations.md create mode 100644 docs/src/explanations/functional-abstraction.md create mode 100644 docs/src/explanations/functional-module-creation.md create mode 100644 docs/src/explanations/interfaces-and-connections.md create mode 100644 docs/src/explanations/memories.md create mode 100644 docs/src/explanations/modules.md create mode 100644 docs/src/explanations/motivation.md create mode 100644 docs/src/explanations/muxes-and-input-selection.md create mode 100644 docs/src/explanations/operators.md create mode 100644 docs/src/explanations/polymorphism-and-parameterization.md create mode 100644 docs/src/explanations/ports.md create mode 100644 docs/src/explanations/printing.md create mode 100644 docs/src/explanations/reset.md create mode 100644 docs/src/explanations/sequential-circuits.md create mode 100644 docs/src/explanations/supported-hardware.md create mode 100644 docs/src/explanations/unconnected-wires.md create mode 100644 docs/src/explanations/width-inference.md (limited to 'docs/src/explanations') diff --git a/docs/src/explanations/annotations.md b/docs/src/explanations/annotations.md index 19d24605..510ebca5 100644 --- a/docs/src/explanations/annotations.md +++ b/docs/src/explanations/annotations.md @@ -4,6 +4,8 @@ title: "Annotations" section: "chisel3" --- +# Annotations + `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. @@ -132,10 +134,8 @@ class ModC(widthC: Int) extends Module { 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 +```scala mdoc:compile-only 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)))) +(new ChiselStage).execute(Array.empty, Seq(ChiselGeneratorAnnotation(() => new ModC(4)))) ``` diff --git a/docs/src/explanations/bundles-and-vecs.md b/docs/src/explanations/bundles-and-vecs.md index bac9393a..78626c42 100644 --- a/docs/src/explanations/bundles-and-vecs.md +++ b/docs/src/explanations/bundles-and-vecs.md @@ -64,7 +64,7 @@ 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). +[Function Constructor](../explanations/functional-module-creation). ### Flipping Bundles diff --git a/docs/src/explanations/combinational-circuits.md b/docs/src/explanations/combinational-circuits.md new file mode 100644 index 00000000..b9e5b8c6 --- /dev/null +++ b/docs/src/explanations/combinational-circuits.md @@ -0,0 +1,84 @@ +--- +layout: docs +title: "Combinational Circuits" +section: "chisel3" +--- + +# Combinational Circuits + +A circuit is represented as a graph of nodes in Chisel. Each node is +a hardware operator that has zero or more inputs and that drives one +output. A literal, introduced above, is a degenerate kind of node +that has no inputs and drives a constant value on its output. One way +to create and wire together nodes is using textual expressions. For +example, we can express a simple combinational logic circuit +using the following expression: + +```scala +(a & b) | (~c & d) +``` + +The syntax should look familiar, with `&` and `|` +representing bitwise-AND and -OR respectively, and `~` +representing bitwise-NOT. The names `a` through `d` +represent named wires of some (unspecified) width. + +Any simple expression can be converted directly into a circuit tree, +with named wires at the leaves and operators forming the internal +nodes. The final circuit output of the expression is taken from the +operator at the root of the tree, in this example, the bitwise-OR. + +Simple expressions can build circuits in the shape of trees, but to +construct circuits in the shape of arbitrary directed acyclic graphs +(DAGs), we need to describe fan-out. In Chisel, we do this by naming +a wire that holds a subexpression that we can then reference multiple +times in subsequent expressions. We name a wire in Chisel by +declaring a variable. For example, consider the select expression, +which is used twice in the following multiplexer description: +```scala +val sel = a | b +val out = (sel & in1) | (~sel & in0) +``` + +The keyword `val` is part of Scala, and is used to name variables +that have values that won't change. It is used here to name the +Chisel wire, `sel`, holding the output of the first bitwise-OR +operator so that the output can be used multiple times in the second +expression. + +### Wires + +Chisel also supports wires as hardware nodes to which one can assign values or connect other nodes. + +```scala +val myNode = Wire(UInt(8.W)) +when (isReady) { + myNode := 255.U +} .otherwise { + myNode := 0.U +} +``` + +```scala +val myNode = Wire(UInt(8.W)) +when (input > 128.U) { + myNode := 255.U +} .elsewhen (input > 64.U) { + myNode := 1.U +} .otherwise { + myNode := 0.U +} +``` + +Note that the last connection to a Wire takes effect. For example, the following two Chisel circuits are equivalent: + +```scala +val myNode = Wire(UInt(8.W)) +myNode := 10.U +myNode := 0.U +``` + +```scala +val myNode = Wire(UInt(8.W)) +myNode := 0.U +``` diff --git a/docs/src/explanations/data-types.md b/docs/src/explanations/data-types.md new file mode 100644 index 00000000..6ac6077b --- /dev/null +++ b/docs/src/explanations/data-types.md @@ -0,0 +1,136 @@ +--- +layout: docs +title: "Data Types" +section: "chisel3" +--- + +# Chisel Data Types + +Chisel datatypes are used to specify the type of values held in state +elements or flowing on wires. While hardware designs ultimately +operate on vectors of binary digits, other more abstract +representations for values allow clearer specifications and help the +tools generate more optimal circuits. In Chisel, a raw collection of +bits is represented by the ```Bits``` type. Signed and unsigned integers +are considered subsets of fixed-point numbers and are represented by +types ```SInt``` and ```UInt``` respectively. Signed fixed-point +numbers, including integers, are represented using two's-complement +format. Boolean values are represented as type ```Bool```. Note +that these types are distinct from Scala's builtin types such as +```Int``` or ```Boolean```. + +> There is a new experimental type **Interval** which gives the developer more control of the type by allowing the definition of an IntervalRange. See: [Interval Type](../appendix/experimental-features#interval-type) + +Additionally, Chisel defines `Bundles` for making +collections of values with named fields (similar to ```structs``` in +other languages), and ```Vecs``` for indexable collections of +values. + +Bundles and Vecs will be covered later. + +Constant or literal values are expressed using Scala integers or +strings passed to constructors for the types: +```scala +1.U // decimal 1-bit lit from Scala Int. +"ha".U // hexadecimal 4-bit lit from string. +"o12".U // octal 4-bit lit from string. +"b1010".U // binary 4-bit lit from string. + +5.S // signed decimal 4-bit lit from Scala Int. +-8.S // negative decimal 4-bit lit from Scala Int. +5.U // unsigned decimal 3-bit lit from Scala Int. + +8.U(4.W) // 4-bit unsigned decimal, value 8. +-152.S(32.W) // 32-bit signed decimal, value -152. + +true.B // Bool lits from Scala lits. +false.B +``` +Underscores can be used as separators in long string literals to aid +readability, but are ignored when creating the value, e.g.: +```scala +"h_dead_beef".U // 32-bit lit of type UInt +``` + +By default, the Chisel compiler will size each constant to the minimum +number of bits required to hold the constant, including a sign bit for +signed types. Bit widths can also be specified explicitly on +literals, as shown below. Note that (`.W` is used to cast a Scala Int +to a Chisel width) +```scala +"ha".asUInt(8.W) // hexadecimal 8-bit lit of type UInt +"o12".asUInt(6.W) // octal 6-bit lit of type UInt +"b1010".asUInt(12.W) // binary 12-bit lit of type UInt + +5.asSInt(7.W) // signed decimal 7-bit lit of type SInt +5.asUInt(8.W) // unsigned decimal 8-bit lit of type UInt +``` + +For literals of type ```UInt```, the value is +zero-extended to the desired bit width. For literals of type +```SInt```, the value is sign-extended to fill the desired bit width. +If the given bit width is too small to hold the argument value, then a +Chisel error is generated. + +>We are working on a more concise literal syntax for Chisel using +symbolic prefix operators, but are stymied by the limitations of Scala +operator overloading and have not yet settled on a syntax that is +actually more readable than constructors taking strings. + +>We have also considered allowing Scala literals to be automatically +converted to Chisel types, but this can cause type ambiguity and +requires an additional import. + +>The SInt and UInt types will also later support an optional exponent +field to allow Chisel to automatically produce optimized fixed-point +arithmetic circuits. + +## Casting + +We can also cast types in Chisel: + +```scala +val sint = 3.S(4.W) // 4-bit SInt + +val uint = sint.asUInt // cast SInt to UInt +uint.asSInt // cast UInt to SInt +``` + +**NOTE**: `asUInt`/`asSInt` with an explicit width can **not** be used to cast (convert) between Chisel datatypes. +No width parameter is accepted, as Chisel will automatically pad or truncate as required when the objects are connected. + +We can also perform casts on clocks, though you should be careful about this, since clocking (especially in ASIC) requires special attention: + +```scala +val bool: Bool = false.B // always-low wire +val clock = bool.asClock // always-low clock + +clock.asUInt // convert clock to UInt (width 1) +clock.asUInt.asBool // convert clock to Bool (Chisel 3.2+) +clock.asUInt.toBool // convert clock to Bool (Chisel 3.0 and 3.1 only) +``` + +## Analog/BlackBox type + +(Experimental, Chisel 3.1+) + +Chisel supports an `Analog` type (equivalent to Verilog `inout`) that can be used to support arbitrary nets in Chisel. This includes analog wires, tri-state/bi-directional wires, and power nets (with appropriate annotations). + +`Analog` is an undirectioned type, and so it is possible to connect multiple `Analog` nets together using the `attach` operator. It is possible to connect an `Analog` **once** using `<>` but illegal to do it more than once. + +```scala +val a = IO(Analog(1.W)) +val b = IO(Analog(1.W)) +val c = IO(Analog(1.W)) + +// Legal +attach(a, b) +attach(a, c) + +// Legal +a <> b + +// Illegal - connects 'a' multiple times +a <> b +a <> c +``` diff --git a/docs/src/explanations/explanations.md b/docs/src/explanations/explanations.md new file mode 100644 index 00000000..01894ad7 --- /dev/null +++ b/docs/src/explanations/explanations.md @@ -0,0 +1,39 @@ +--- +layout: docs +title: "Explanations" +section: "chisel3" +--- + +# Explanations + +Explanation documentation gives background and context. +They can also explain why things are so - design decisions, +historical reasons, technical constraints. + +If you are just getting started with Chisel, we suggest you +read these documents in the following order: + +* [Motivation](motivation) +* [Supported Hardware](supported-hardware) +* [Data Types](data-types) +* [Bundles and Vecs](bundles-and-vecs) +* [Combinational Circuits](combinational-circuits) +* [Operators](operators) +* [Width Inference](width-inference) +* [Functional Abstraction](functional-abstraction) +* [Ports](ports) +* [Modules](modules) +* [Sequential Circuits](sequential-circuits) +* [Memories](memories) +* [Interfaces and Connections](interfaces-and-connections) +* [Black Boxes](blackboxes) +* [Enumerations](enumerations) +* [Functional Module Creation](functional-module-creation) +* [Muxes and Input Selection](muxes-and-input-selection) +* [Multiple Clock Domains](multi-clock) +* [Reset](reset) +* [Polymorphism and Paramterization](polymorphism-and-parameterization) +* [Printing in Chisel](printing) +* [Naming](naming) +* [Unconnected Wires](unconnected-wires) +* [Annotations](annotations) diff --git a/docs/src/explanations/functional-abstraction.md b/docs/src/explanations/functional-abstraction.md new file mode 100644 index 00000000..4e3900b6 --- /dev/null +++ b/docs/src/explanations/functional-abstraction.md @@ -0,0 +1,34 @@ +--- +layout: docs +title: "Functional Abstraction" +section: "chisel3" +--- + +# Functional Abstraction + +We can define functions to factor out a repeated piece of logic that +we later reuse multiple times in a design. For example, we can wrap +up our earlier example of a simple combinational logic block as +follows: + +```scala mdoc:invisible +import chisel3._ +``` + +```scala mdoc:silent +def clb(a: UInt, b: UInt, c: UInt, d: UInt): UInt = + (a & b) | (~c & d) +``` + +where ```clb``` is the function which takes ```a```, ```b```, +```c```, ```d``` as arguments and returns a wire to the output of a +boolean circuit. The ```def``` keyword is part of Scala and +introduces a function definition, with each argument followed by a colon then its type, +and the function return type given after the colon following the +argument list. The equals (```=})```sign separates the function argument list from the function +definition. + +We can then use the block in another circuit as follows: +```scala mdoc:silent +val out = clb(a,b,c,d) +``` diff --git a/docs/src/explanations/functional-module-creation.md b/docs/src/explanations/functional-module-creation.md new file mode 100644 index 00000000..407edb1d --- /dev/null +++ b/docs/src/explanations/functional-module-creation.md @@ -0,0 +1,120 @@ +--- +layout: docs +title: "Functional Module Creation" +section: "chisel3" +--- + +# Functional Module Creation + +Objects in Scala have a pre-existing creation function (method) called `apply`. +When an object is used as value in an expression (which basically means that the constructor was called), this method determines the returned value. +When dealing with hardware modules, one would expect the module output to be representative of the hardware module's functionality. +Therefore, we would sometimes like the module output to be the value returned when using the object as a value in an expression. +Since hardware modules are represented as Scala objects, this can be done by defining the object's `apply` method to return the module's output. +This can be referred to as creating a functional interface for module construction. +If we apply this on the standard mux2 example, we would to return the mux2 output ports when we used mux2 in an expression. +Implementing this requires building a constructor that takes multiplexer inputs as parameters and returns the multiplexer output: + +```scala mdoc:silent +import chisel3._ + +class Mux2 extends Module { + val io = IO(new Bundle { + val sel = Input(Bool()) + val in0 = Input(UInt()) + val in1 = Input(UInt()) + val out = Output(UInt()) + }) + io.out := Mux(io.sel, io.in0, io.in1) +} + +object Mux2 { + def apply(sel: UInt, in0: UInt, in1: UInt) = { + val m = Module(new Mux2) + m.io.in0 := in0 + m.io.in1 := in1 + m.io.sel := sel + m.io.out + } +} +``` + +As we can see in the code example, we defined the `apply` method to take the Mux2 inputs as the method parameters, and return the Mux2 output as the function's return value. +By defining modules in this way, it is easier to later implement larger and more complex version of this regular module. +For example, we previously implemented Mux4 like this: + +```scala mdoc:silent +class Mux4 extends Module { + val io = IO(new Bundle { + val in0 = Input(UInt(1.W)) + val in1 = Input(UInt(1.W)) + val in2 = Input(UInt(1.W)) + val in3 = Input(UInt(1.W)) + val sel = Input(UInt(2.W)) + val out = Output(UInt(1.W)) + }) + val m0 = Module(new Mux2) + m0.io.sel := io.sel(0) + m0.io.in0 := io.in0 + m0.io.in1 := io.in1 + + val m1 = Module(new Mux2) + m1.io.sel := io.sel(0) + m1.io.in0 := io.in2 + m1.io.in1 := io.in3 + + val m3 = Module(new Mux2) + m3.io.sel := io.sel(1) + m3.io.in0 := m0.io.out + m3.io.in1 := m1.io.out + + io.out := m3.io.out +} +``` + +However, by using the creation function we redefined for Mux2, we can now use the Mux2 outputs as values of the modules themselves +when writing the Mux4 output expression: + +```scala mdoc:invisible:reset +// We need to re-do this to allow us to `reset` +// and then re-define Mux4 +import chisel3._ + +class Mux2 extends Module { + val io = IO(new Bundle { + val sel = Input(Bool()) + val in0 = Input(UInt()) + val in1 = Input(UInt()) + val out = Output(UInt()) + }) + io.out := Mux(io.sel, io.in0, io.in1) +} + +object Mux2 { + def apply(sel: UInt, in0: UInt, in1: UInt) = { + val m = Module(new Mux2) + m.io.in0 := in0 + m.io.in1 := in1 + m.io.sel := sel + m.io.out + } +} +``` + +```scala mdoc:silent +class Mux4 extends Module { + val io = IO(new Bundle { + val in0 = Input(UInt(1.W)) + val in1 = Input(UInt(1.W)) + val in2 = Input(UInt(1.W)) + val in3 = Input(UInt(1.W)) + val sel = Input(UInt(2.W)) + val out = Output(UInt(1.W)) + }) + io.out := Mux2(io.sel(1), + Mux2(io.sel(0), io.in0, io.in1), + Mux2(io.sel(0), io.in2, io.in3)) +} +``` + +This allows us to write more intuitively readable hardware connection descriptions, which are similar to software expression evaluation. diff --git a/docs/src/explanations/interfaces-and-connections.md b/docs/src/explanations/interfaces-and-connections.md new file mode 100644 index 00000000..9f48b642 --- /dev/null +++ b/docs/src/explanations/interfaces-and-connections.md @@ -0,0 +1,149 @@ +--- +layout: docs +title: "Interfaces and Connections" +section: "chisel3" +--- + +# Interfaces & Bulk Connections + +For more sophisticated modules it is often useful to define and instantiate interface classes while defining the IO for a module. First and foremost, interface classes promote reuse allowing users to capture once and for all common interfaces in a useful form. + +Secondly, interfaces allow users to dramatically reduce wiring by supporting bulk connections between producer and consumer modules. Finally, users can make changes in large interfaces in one place reducing the number of updates required when adding or removing pieces of the interface. + +Note that Chisel has some built-in standard interface which should be used whenever possible for interoperability (e.g. Decoupled). + +## Ports: Subclasses & Nesting + +As we saw earlier, users can define their own interfaces by defining a class that subclasses Bundle. For example, a user could define a simple link for hand-shaking data as follows: + +```scala mdoc:invisible +import chisel3._ +``` + +```scala mdoc:silent +class SimpleLink extends Bundle { + val data = Output(UInt(16.W)) + val valid = Output(Bool()) +} +``` + +We can then extend SimpleLink by adding parity bits using bundle inheritance: +```scala mdoc:silent +class PLink extends SimpleLink { + val parity = Output(UInt(5.W)) +} +``` +In general, users can organize their interfaces into hierarchies using inheritance. + +From there we can define a filter interface by nesting two PLinks into a new FilterIO bundle: +```scala mdoc:silent +class FilterIO extends Bundle { + val x = Flipped(new PLink) + val y = new PLink +} +``` +where flip recursively changes the direction of a bundle, changing input to output and output to input. + +We can now define a filter by defining a filter class extending module: +```scala mdoc:silent +class Filter extends Module { + val io = IO(new FilterIO) + // ... +} +``` +where the io field contains FilterIO. + +## Bundle Vectors + +Beyond single elements, vectors of elements form richer hierarchical interfaces. For example, in order to create a crossbar with a vector of inputs, producing a vector of outputs, and selected by a UInt input, we utilize the Vec constructor: +```scala mdoc:silent +import chisel3.util.log2Ceil +class CrossbarIo(n: Int) extends Bundle { + val in = Vec(n, Flipped(new PLink)) + val sel = Input(UInt(log2Ceil(n).W)) + val out = Vec(n, new PLink) +} +``` +where Vec takes a size as the first argument and a block returning a port as the second argument. + +## Bulk Connections + +We can now compose two filters into a filter block as follows: +```scala mdoc:silent +class Block extends Module { + val io = IO(new FilterIO) + val f1 = Module(new Filter) + val f2 = Module(new Filter) + f1.io.x <> io.x + f1.io.y <> f2.io.x + f2.io.y <> io.y +} +``` +where <> bulk connects interfaces of opposite gender between sibling modules or interfaces of the same gender between parent/child modules. + +Bulk connections connect leaf ports of the same name to each other. If the names do not match or are missing, Chisel does not generate a connection. + +Caution: bulk connections should only be used with **directioned elements** (like IOs), and is not magical (e.g. connecting two wires isn't supported since Chisel can't necessarily figure out the directions automatically [chisel3#603](https://github.com/freechipsproject/chisel3/issues/603)). + +## The standard ready-valid interface (ReadyValidIO / Decoupled) + +Chisel provides a standard interface for [ready-valid interfaces](http://inst.eecs.berkeley.edu/~cs150/Documents/Interfaces.pdf). +A ready-valid interface consists of a `ready` signal, a `valid` signal, and some data stored in `bits`. +The `ready` bit indicates that a consumer is *ready* to consume data. +The `valid` bit indicates that a producer has *valid* data on `bits`. +When both `ready` and `valid` are asserted, a data transfer from the producer to the consumer takes place. +A convenience method `fire` is provided that is asserted if both `ready` and `valid` are asserted. + +Usually, we use the utility function [`Decoupled()`](https://chisel.eecs.berkeley.edu/api/latest/chisel3/util/Decoupled$.html) to turn any type into a ready-valid interface rather than directly using [ReadyValidIO](http://chisel.eecs.berkeley.edu/api/latest/chisel3/util/ReadyValidIO.html). + +* `Decoupled(...)` creates a producer / output ready-valid interface (i.e. bits is an output). +* `Flipped(Decoupled(...))` creates a consumer / input ready-valid interface (i.e. bits is an input). + +Take a look at the following example Chisel code to better understand exactly what is generated: + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.util.Decoupled + +/** + * Using Decoupled(...) creates a producer interface. + * i.e. it has bits as an output. + * This produces the following ports: + * input io_readyValid_ready, + * output io_readyValid_valid, + * output [31:0] io_readyValid_bits + */ +class ProducingData extends Module { + val io = IO(new Bundle { + val readyValid = Decoupled(UInt(32.W)) + }) + // do something with io.readyValid.ready + io.readyValid.valid := true.B + io.readyValid.bits := 5.U +} + +/** + * Using Flipped(Decoupled(...)) creates a consumer interface. + * i.e. it has bits as an input. + * This produces the following ports: + * output io_readyValid_ready, + * input io_readyValid_valid, + * input [31:0] io_readyValid_bits + */ +class ConsumingData extends Module { + val io = IO(new Bundle { + val readyValid = Flipped(Decoupled(UInt(32.W))) + }) + io.readyValid.ready := false.B + // do something with io.readyValid.valid + // do something with io.readyValid.bits +} +``` + +`DecoupledIO` is a ready-valid interface with the *convention* that there are no guarantees placed on deasserting `ready` or `valid` or on the stability of `bits`. +That means `ready` and `valid` can also be deasserted without a data transfer. + +`IrrevocableIO` is a ready-valid interface with the *convention* that the value of `bits` will not change while `valid` is asserted and `ready` is deasserted. +Also the consumer shall keep `ready` asserted after a cycle where `ready` was high and `valid` was low. +Note that the *irrevocable* constraint *is only a convention* and cannot be enforced by the interface. +Chisel does not automatically generate checkers or assertions to enforce the *irrevocable* convention. diff --git a/docs/src/explanations/memories.md b/docs/src/explanations/memories.md new file mode 100644 index 00000000..792d176e --- /dev/null +++ b/docs/src/explanations/memories.md @@ -0,0 +1,176 @@ +--- +layout: docs +title: "Memories" +section: "chisel3" +--- + +# Memories + +Chisel provides facilities for creating both read only and read/write memories. + +## ROM + +Users can define read only memories with a `Vec`: +```scala mdoc:invisible +import chisel3._ +``` +``` scala mdoc:compile-only + VecInit(inits: Seq[T]) + VecInit(elt0: T, elts: T*) +``` + +where `inits` is a sequence of initial `Data` literals that initialize the ROM. For example, users cancreate a small ROM initialized to 1, 2, 4, 8 and loop through all values using a counter as an address generator as follows: + +``` scala mdoc:compile-only + val m = VecInit(Array(1.U, 2.U, 4.U, 8.U)) + val r = m(counter(m.length.U)) +``` + +We can create an *n* value sine lookup table using a ROM initialized as follows: + +``` scala mdoc:silent + def sinTable(amp: Double, n: Int) = { + val times = + (0 until n).map(i => (i*2*Pi)/(n.toDouble-1) - Pi) + val inits = + times.map(t => round(amp * sin(t)).asSInt(32.W)) + VecInit(inits) + } + def sinWave(amp: Double, n: Int) = + sinTable(amp, n)(counter(n.U)) +``` + +where `amp` is used to scale the fixpoint values stored in the ROM. + +## Read-Write Memories + +Memories are given special treatment in Chisel since hardware implementations of memory vary greatly. For example, FPGA memories are instantiated quite differently from ASIC memories. Chisel defines a memory abstraction that can map to either simple Verilog behavioural descriptions or to instances of memory modules that are available from external memory generators provided by foundry or IP vendors. + + +### `SyncReadMem`: sequential/synchronous-read, sequential/synchronous-write + +Chisel has a construct called `SyncReadMem` for sequential/synchronous-read, sequential/synchronous-write memories. These `SyncReadMem`s will likely be synthesized to technology SRAMs (as opposed to register banks). + +If the same memory address is both written and sequentially read on the same clock edge, or if a sequential read enable is cleared, then the read data is undefined. + +Values on the read data port are not guaranteed to be held until the next read cycle. If that is the desired behavior, external logic to hold the last read value must be added. + +#### Read port/write port +Ports into `SyncReadMem`s are created by applying a `UInt` index. A 1024-entry SRAM with one write port and one read port might be expressed as follows: + +```scala mdoc:silent +import chisel3._ +class ReadWriteSmem extends Module { + val width: Int = 32 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val addr = Input(UInt(10.W)) + val dataIn = Input(UInt(width.W)) + val dataOut = Output(UInt(width.W)) + }) + + val mem = SyncReadMem(1024, UInt(width.W)) + // Create one write port and one read port + mem.write(io.addr, io.dataIn) + io.dataOut := mem.read(io.addr, io.enable) +} +``` + +Below is an example waveform of the one write port/one read port `SyncReadMem` with [masks](#masks). Note that the signal names will differ from the exact wire names generated for the `SyncReadMem`. With masking, it is also possible that multiple RTL arrays will be generated with the behavior below. + +![read/write ports example waveform](https://svg.wavedrom.com/github/freechipsproject/www.chisel-lang.org/master/docs/src/main/tut/chisel3/memories_waveforms/smem_read_write.json) + +#### Single-ported +Single-ported SRAMs can be inferred when the read and write conditions are mutually exclusive in the same `when` chain: + +```scala mdoc:silent +import chisel3._ +class RWSmem extends Module { + val width: Int = 32 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val addr = Input(UInt(10.W)) + val dataIn = Input(UInt(width.W)) + val dataOut = Output(UInt(width.W)) + }) + + val mem = SyncReadMem(1024, UInt(width.W)) + io.dataOut := DontCare + when(io.enable) { + val rdwrPort = mem(io.addr) + when (io.write) { rdwrPort := io.dataIn } + .otherwise { io.dataOut := rdwrPort } + } +} +``` + +(The `DontCare` is there to make Chisel's [unconnected wire detection](unconnected-wires) aware that reading while writing is undefined.) + +Here is an example single read/write port waveform, with [masks](#masks) (again, generated signal names and number of arrays may differ): + +![read/write ports example waveform](https://svg.wavedrom.com/github/freechipsproject/www.chisel-lang.org/master/docs/src/main/tut/chisel3/memories_waveforms/smem_rw.json) + +### `Mem`: combinational/asynchronous-read, sequential/synchronous-write + +Chisel supports random-access memories via the `Mem` construct. Writes to `Mem`s are combinational/asynchronous-read, sequential/synchronous-write. These `Mem`s will likely be synthesized to register banks, since most SRAMs in modern technologies (FPGA, ASIC) tend to no longer support combinational (asynchronous) reads. + +Creating asynchronous-read versions of the examples above simply involves replacing `SyncReadMem` with `Mem`. + +### Masks + +Chisel memories also support write masks for subword writes. Chisel will infer masks if the data type of the memory is a vector. To infer a mask, specify the `mask` argument of the `write` function which creates write ports. A given masked length is written if the corresponding mask bit is set. For example, in the example below, if the 0th bit of mask is true, it will write the lower byte of the data at corresponding address. + +```scala mdoc:silent +import chisel3._ +class MaskedReadWriteSmem extends Module { + val width: Int = 8 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val addr = Input(UInt(10.W)) + val mask = Input(Vec(4, Bool())) + val dataIn = Input(Vec(4, UInt(width.W))) + val dataOut = Output(Vec(4, UInt(width.W))) + }) + + // Create a 32-bit wide memory that is byte-masked + val mem = SyncReadMem(1024, Vec(4, UInt(width.W))) + // Write with mask + mem.write(io.addr, io.dataIn, io.mask) + io.dataOut := mem.read(io.addr, io.enable) +} +``` + +Here is an example of masks with readwrite ports: + +```scala mdoc:silent +import chisel3._ +class MaskedRWSmem extends Module { + val width: Int = 32 + val io = IO(new Bundle { + val enable = Input(Bool()) + val write = Input(Bool()) + val mask = Input(Vec(2, Bool())) + val addr = Input(UInt(10.W)) + val dataIn = Input(Vec(2, UInt(width.W))) + val dataOut = Output(Vec(2, UInt(width.W))) + }) + + val mem = SyncReadMem(1024, Vec(2, UInt(width.W))) + io.dataOut := DontCare + when(io.enable) { + val rdwrPort = mem(io.addr) + when (io.write) { + when(io.mask(0)) { + rdwrPort(0) := io.dataIn(0) + } + when(io.mask(1)) { + rdwrPort(1) := io.dataIn(1) + } + }.otherwise { io.dataOut := rdwrPort } + } +} +``` + diff --git a/docs/src/explanations/modules.md b/docs/src/explanations/modules.md new file mode 100644 index 00000000..32cbff2e --- /dev/null +++ b/docs/src/explanations/modules.md @@ -0,0 +1,138 @@ +--- +layout: docs +title: "Modules" +section: "chisel3" +--- + +# Modules + +Chisel *modules* are very similar to Verilog *modules* in +defining a hierarchical structure in the generated circuit. + +The hierarchical module namespace is accessible in downstream tools +to aid in debugging and physical layout. A user-defined module is +defined as a *class* which: + + - inherits from `Module`, + - contains at least one interface wrapped in a Module's `IO()` method (traditionally stored in a port field named ```io```), and + - wires together subcircuits in its constructor. + +As an example, consider defining your own two-input multiplexer as a +module: +```scala mdoc:silent +import chisel3._ +class Mux2IO extends Bundle { + val sel = Input(UInt(1.W)) + val in0 = Input(UInt(1.W)) + val in1 = Input(UInt(1.W)) + val out = Output(UInt(1.W)) +} + +class Mux2 extends Module { + val io = IO(new Mux2IO) + io.out := (io.sel & io.in1) | (~io.sel & io.in0) +} +``` + +The wiring interface to a module is a collection of ports in the +form of a ```Bundle```. The interface to the module is defined +through a field named ```io```. For ```Mux2```, ```io``` is +defined as a bundle with four fields, one for each multiplexer port. + +The ```:=``` assignment operator, used here in the body of the +definition, is a special operator in Chisel that wires the input of +left-hand side to the output of the right-hand side. + +### Module Hierarchy + +We can now construct circuit hierarchies, where we build larger modules out +of smaller sub-modules. For example, we can build a 4-input +multiplexer module in terms of the ```Mux2``` module by wiring +together three 2-input multiplexers: + +```scala mdoc:silent +class Mux4IO extends Bundle { + val in0 = Input(UInt(1.W)) + val in1 = Input(UInt(1.W)) + val in2 = Input(UInt(1.W)) + val in3 = Input(UInt(1.W)) + val sel = Input(UInt(2.W)) + val out = Output(UInt(1.W)) +} +class Mux4 extends Module { + val io = IO(new Mux4IO) + + val m0 = Module(new Mux2) + m0.io.sel := io.sel(0) + m0.io.in0 := io.in0 + m0.io.in1 := io.in1 + + val m1 = Module(new Mux2) + m1.io.sel := io.sel(0) + m1.io.in0 := io.in2 + m1.io.in1 := io.in3 + + val m3 = Module(new Mux2) + m3.io.sel := io.sel(1) + m3.io.in0 := m0.io.out + m3.io.in1 := m1.io.out + + io.out := m3.io.out +} +``` + +We again define the module interface as ```io``` and wire up the +inputs and outputs. In this case, we create three ```Mux2``` +children modules, using the ```Module``` constructor function and +the Scala ```new``` keyword to create a +new object. We then wire them up to one another and to the ports of +the ```Mux4``` interface. + +Note: Chisel `Module`s have an implicit clock (called `clock`) and +an implicit reset (called `reset`). To create modules without implicit +clock and reset, Chisel provides `RawModule`. + +> Historical Note: Prior to Chisel 3.5, Modules were restricted to only +having a single user-defined port named `io`. There was also a type called +`MultiIOModule` that provided implicit clock and reset while allowing the +user to define as many ports as they want. This is now the functionality +of `Module`. + +### `RawModule` + +A `RawModule` is a module that **does not provide an implicit clock and reset.** +This can be useful when interfacing a Chisel module with a design that expects +a specific naming convention for clock or reset. + +Then we can use it in place of *Module* usage : +```scala mdoc:silent +import chisel3.{RawModule, withClockAndReset} + +class Foo extends Module { + val io = IO(new Bundle{ + val a = Input(Bool()) + val b = Output(Bool()) + }) + io.b := !io.a +} + +class FooWrapper extends RawModule { + val a_i = IO(Input(Bool())) + val b_o = IO(Output(Bool())) + val clk = Input(Clock()) + val rstn = Input(Bool()) + + val foo = withClockAndReset(clk, !rstn){ Module(new Foo) } + + foo.io.a := a_i + b_o := foo.io.b +} +``` + +In the example above, the `RawModule` is used to change the reset polarity +of module `SlaveSpi`. Indeed, the reset is active high by default in Chisel +modules, then using `withClockAndReset(clock, !rstn)` we can use an active low +reset in entire design. + +The clock is just wired as it, but if needed, `RawModule` can be used in +conjunction with `BlackBox` to connect a differential clock input for example. diff --git a/docs/src/explanations/motivation.md b/docs/src/explanations/motivation.md new file mode 100644 index 00000000..afe02a7b --- /dev/null +++ b/docs/src/explanations/motivation.md @@ -0,0 +1,44 @@ +--- +layout: docs +title: "Motivation" +section: "chisel3" +--- + +# Motivation -- "Why Chisel?" + +We were motivated to develop a new hardware language by years of +struggle with existing hardware description languages in our research +projects and hardware design courses. _Verilog_ and _VHDL_ were developed +as hardware _simulation_ languages, and only later did they become +a basis for hardware _synthesis_. Much of the semantics of these +languages are not appropriate for hardware synthesis and, in fact, +many constructs are simply not synthesizable. Other constructs are +non-intuitive in how they map to hardware implementations, or their +use can accidentally lead to highly inefficient hardware structures. +While it is possible to use a subset of these languages and still get +acceptable results, they nonetheless present a cluttered and confusing +specification model, particularly in an instructional setting. + +However, our strongest motivation for developing a new hardware +language is our desire to change the way that electronic system design +takes place. We believe that it is important to not only teach +students how to design circuits, but also to teach them how to design +*circuit generators* ---programs that automatically generate +designs from a high-level set of design parameters and constraints. +Through circuit generators, we hope to leverage the hard work of +design experts and raise the level of design abstraction for everyone. +To express flexible and scalable circuit construction, circuit +generators must employ sophisticated programming techniques to make +decisions concerning how to best customize their output circuits +according to high-level parameter values and constraints. While +Verilog and VHDL include some primitive constructs for programmatic +circuit generation, they lack the powerful facilities present in +modern programming languages, such as object-oriented programming, +type inference, support for functional programming, and reflection. + +Instead of building a new hardware design language from scratch, we +chose to embed hardware construction primitives within an existing +language. We picked Scala not only because it includes the +programming features we feel are important for building circuit +generators, but because it was specifically developed as a base for +domain-specific languages. diff --git a/docs/src/explanations/muxes-and-input-selection.md b/docs/src/explanations/muxes-and-input-selection.md new file mode 100644 index 00000000..ae087e83 --- /dev/null +++ b/docs/src/explanations/muxes-and-input-selection.md @@ -0,0 +1,62 @@ +--- +layout: docs +title: "Muxes and Input Selection" +section: "chisel3" +--- + +# Muxes and Input Selection + +Selecting inputs is very useful in hardware description, and therefore Chisel provides several built-in generic input-selection implementations. + +### Mux +The first one is `Mux`. This is a 2-input selector. Unlike the `Mux2` example which was presented previously, the built-in `Mux` allows +the inputs (`in0` and `in1`) to be any datatype as long as they are the same subclass of `Data`. + +By using the functional module creation feature presented in the previous section, we can create multi-input selector in a simple way: + +```scala +Mux(c1, a, Mux(c2, b, Mux(..., default))) +``` + +### MuxCase + +The nested `Mux` is not necessary since Chisel also provides the built-in `MuxCase`, which implements that exact feature. +`MuxCase` is an n-way `Mux`, which can be used as follows: + +```scala +MuxCase(default, Array(c1 -> a, c2 -> b, ...)) +``` + +Where each selection dependency is represented as a tuple in a Scala +array [ condition -> selected_input_port ]. + +### MuxLookup +Chisel also provides `MuxLookup` which is an n-way indexed multiplexer: + +```scala +MuxLookup(idx, default, + Array(0.U -> a, 1.U -> b, ...)) +``` + +This is the same as a `MuxCase`, where the conditions are all index based selection: + +```scala +MuxCase(default, + Array((idx === 0.U) -> a, + (idx === 1.U) -> b, ...)) +``` + +Note that the conditions/cases/selectors (eg. c1, c2) must be in parentheses. + +### Mux1H +Another ```Mux``` utility is the one-hot mux, ```Mux1H```. It takes a sequence of selectors and values and returns the value associated with the one selector that is set. If zero or multiple selectors are set the behavior is undefined. For example: + +```scala + val hotValue = chisel3.util.Mux1H(Seq( + io.selector(0) -> 2.U, + io.selector(1) -> 4.U, + io.selector(2) -> 8.U, + io.selector(4) -> 11.U, + )) +``` +```Mux1H``` whenever possible generates *Firrtl* that is readily optimizable as low depth and/or tree. This optimization is not possible when the values are of type ```FixedPoint``` or an aggregate type that contains ```FixedPoint```s and results instead as a simple ```Mux``` tree. This behavior could be sub-optimal. As ```FixedPoint``` is still *experimental* this behavior may change in the future. diff --git a/docs/src/explanations/operators.md b/docs/src/explanations/operators.md new file mode 100644 index 00000000..231a53fa --- /dev/null +++ b/docs/src/explanations/operators.md @@ -0,0 +1,65 @@ +--- +layout: docs +title: "Operators" +section: "chisel3" +--- + +# Chisel Operators + +Chisel defines a set of hardware operators: + +| Operation | Explanation | +| --------- | --------- | +| **Bitwise operators** | **Valid on:** SInt, UInt, Bool | +| `val invertedX = ~x` | Bitwise NOT | +| `val hiBits = x & "h_ffff_0000".U` | Bitwise AND | +| `val flagsOut = flagsIn \| overflow` | Bitwise OR | +| `val flagsOut = flagsIn ^ toggle` | Bitwise XOR | +| **Bitwise reductions.** | **Valid on:** SInt and UInt. Returns Bool. | +| `val allSet = x.andR` | AND reduction | +| `val anySet = x.orR` | OR reduction | +| `val parity = x.xorR` | XOR reduction | +| **Equality comparison.** | **Valid on:** SInt, UInt, and Bool. Returns Bool. | +| `val equ = x === y` | Equality | +| `val neq = x =/= y` | Inequality | +| **Shifts** | **Valid on:** SInt and UInt | +| `val twoToTheX = 1.S << x` | Logical shift left | +| `val hiBits = x >> 16.U` | Right shift (logical on UInt and arithmetic on SInt). | +| **Bitfield manipulation** | **Valid on:** SInt, UInt, and Bool. | +| `val xLSB = x(0)` | Extract single bit, LSB has index 0. | +| `val xTopNibble = x(15, 12)` | Extract bit field from end to start bit position. | +| `val usDebt = Fill(3, "hA".U)` | Replicate a bit string multiple times. | +| `val float = Cat(sign, exponent, mantissa)` | Concatenates bit fields, with first argument on left. | +| **Logical Operations** | **Valid on:** Bool +| `val sleep = !busy` | Logical NOT | +| `val hit = tagMatch && valid` | Logical AND | +| `val stall = src1busy || src2busy` | Logical OR | +| `val out = Mux(sel, inTrue, inFalse)` | Two-input mux where sel is a Bool | +| **Arithmetic operations** | **Valid on Nums:** SInt and UInt. | +| `val sum = a + b` *or* `val sum = a +% b` | Addition (without width expansion) | +| `val sum = a +& b` | Addition (with width expansion) | +| `val diff = a - b` *or* `val diff = a -% b` | Subtraction (without width expansion) | +| `val diff = a -& b` | Subtraction (with width expansion) | +| `val prod = a * b` | Multiplication | +| `val div = a / b` | Division | +| `val mod = a % b` | Modulus | +| **Arithmetic comparisons** | **Valid on Nums:** SInt and UInt. Returns Bool. | +| `val gt = a > b` | Greater than | +| `val gte = a >= b` | Greater than or equal | +| `val lt = a < b` | Less than | +| `val lte = a <= b` | Less than or equal | + +>Our choice of operator names was constrained by the Scala language. +We have to use triple equals```===``` for equality and ```=/=``` +for inequality to allow the +native Scala equals operator to remain usable. + +The Chisel operator precedence is not directly defined as part of the Chisel language. +Practically, it is determined by the evaluation order of the circuit, +which natuarally follows the [Scala operator precedence](https://docs.scala-lang.org/tour/operators.html). +If in doubt of operator precedence, use parentheses. + +> The Chisel/Scala operator precedence is similar but +not identical to precedence in Java or C. Verilog has the same operator precedence as C, but VHDL +does not. Verilog has precedence ordering for logic operations, but in VHDL +those operators have the same precedence and are evaluated from left to right. diff --git a/docs/src/explanations/polymorphism-and-parameterization.md b/docs/src/explanations/polymorphism-and-parameterization.md new file mode 100644 index 00000000..94b896b1 --- /dev/null +++ b/docs/src/explanations/polymorphism-and-parameterization.md @@ -0,0 +1,237 @@ +--- +layout: docs +title: "Polymorphism and Parameterization" +section: "chisel3" +--- + +# Polymorphism and Paramterization + +_This section is advanced and can be skipped at first reading._ + +Scala is a strongly typed language and uses parameterized types to specify generic functions and classes. +In this section, we show how Chisel users can define their own reusable functions and classes using parameterized classes. + +## Parameterized Functions + +Earlier we defined `Mux2` on `Bool`, but now we show how we can define a generic multiplexer function. +We define this function as taking a boolean condition and con and alt arguments (corresponding to then and else expressions) of type `T`: + +```scala +def Mux[T <: Bits](c: Bool, con: T, alt: T): T = { ... } +``` + +where `T` is required to be a subclass of `Bits`. +Scala ensures that in each usage of `Mux`, it can find a common superclass of the actual con and alt argument types, +otherwise it causes a Scala compilation type error. +For example, + +```scala +Mux(c, UInt(10), UInt(11)) +``` + +yields a `UInt` wire because the `con` and `alt` arguments are each of type `UInt`. + + + +## Parameterized Classes + +Like parameterized functions, we can also parameterize classes to make them more reusable. +For instance, we can generalize the Filter class to use any kind of link. +We do so by parameterizing the `FilterIO` class and defining the constructor to take a single argument `gen` of type `T` as below. +```scala mdoc:invisible +import chisel3._ +``` +```scala mdoc:silent +class FilterIO[T <: Data](gen: T) extends Bundle { + val x = Input(gen) + val y = Output(gen) +} +``` + +We can now define `Filter` by defining a module class that also takes a link type constructor argument and passes it through to the `FilterIO` interface constructor: + +```scala mdoc:silent +class Filter[T <: Data](gen: T) extends Module { + val io = IO(new FilterIO(gen)) + // ... +} +``` + +We can now define a `PLink`-based `Filter` as follows: +```scala mdoc:invisible +class SimpleLink extends Bundle { + val data = Output(UInt(16.W)) + val valid = Output(Bool()) +} +class PLink extends SimpleLink { + val parity = Output(UInt(5.W)) +} +``` +```scala mdoc:compile-only +val f = Module(new Filter(new PLink)) +``` + +A generic FIFO could be defined as follows: + +```scala mdoc:silent +import chisel3.util.log2Up + +class DataBundle extends Bundle { + val a = UInt(32.W) + val b = UInt(32.W) +} + +class Fifo[T <: Data](gen: T, n: Int) extends Module { + val io = IO(new Bundle { + val enqVal = Input(Bool()) + val enqRdy = Output(Bool()) + val deqVal = Output(Bool()) + val deqRdy = Input(Bool()) + val enqDat = Input(gen) + val deqDat = Output(gen) + }) + val enqPtr = RegInit(0.U((log2Up(n)).W)) + val deqPtr = RegInit(0.U((log2Up(n)).W)) + val isFull = RegInit(false.B) + val doEnq = io.enqRdy && io.enqVal + val doDeq = io.deqRdy && io.deqVal + val isEmpty = !isFull && (enqPtr === deqPtr) + val deqPtrInc = deqPtr + 1.U + val enqPtrInc = enqPtr + 1.U + val isFullNext = Mux(doEnq && ~doDeq && (enqPtrInc === deqPtr), + true.B, Mux(doDeq && isFull, false.B, + isFull)) + enqPtr := Mux(doEnq, enqPtrInc, enqPtr) + deqPtr := Mux(doDeq, deqPtrInc, deqPtr) + isFull := isFullNext + val ram = Mem(n, gen) + when (doEnq) { + ram(enqPtr) := io.enqDat + } + io.enqRdy := !isFull + io.deqVal := !isEmpty + ram(deqPtr) <> io.deqDat +} +``` + +An Fifo with 8 elements of type DataBundle could then be instantiated as: + +```scala mdoc:compile-only +val fifo = Module(new Fifo(new DataBundle, 8)) +``` + +It is also possible to define a generic decoupled (ready/valid) interface: +```scala mdoc:invisible:reset +import chisel3._ +class DataBundle extends Bundle { + val a = UInt(32.W) + val b = UInt(32.W) +} +``` + +```scala mdoc:silent +class DecoupledIO[T <: Data](data: T) extends Bundle { + val ready = Input(Bool()) + val valid = Output(Bool()) + val bits = Output(data) +} +``` + +This template can then be used to add a handshaking protocol to any +set of signals: + +```scala mdoc:silent +class DecoupledDemo extends DecoupledIO(new DataBundle) +``` + +The FIFO interface can be now be simplified as follows: + +```scala mdoc:silent +class Fifo[T <: Data](data: T, n: Int) extends Module { + val io = IO(new Bundle { + val enq = Flipped(new DecoupledIO(data)) + val deq = new DecoupledIO(data) + }) + // ... +} +``` + +## Parametrization based on Modules + +You can also parametrize modules based on other modules rather than just types. The following is an example of a module parametrized by other modules as opposed to e.g. types. + +```scala mdoc:silent +import chisel3.RawModule +import chisel3.experimental.BaseModule +import chisel3.stage.ChiselStage + +// Provides a more specific interface since generic Module +// provides no compile-time information on generic module's IOs. +trait MyAdder { + def in1: UInt + def in2: UInt + def out: UInt +} + +class Mod1 extends RawModule with MyAdder { + val in1 = IO(Input(UInt(8.W))) + val in2 = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in1 + in2 +} + +class Mod2 extends RawModule with MyAdder { + val in1 = IO(Input(UInt(8.W))) + val in2 = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in1 - in2 +} + +class X[T <: BaseModule with MyAdder](genT: => T) extends Module { + val io = IO(new Bundle { + val in1 = Input(UInt(8.W)) + val in2 = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + val subMod = Module(genT) + io.out := subMod.out + subMod.in1 := io.in1 + subMod.in2 := io.in2 +} + +println(ChiselStage.emitVerilog(new X(new Mod1))) +println(ChiselStage.emitVerilog(new X(new Mod2))) +``` +```scala mdoc:passthrough +println(ChiselStage.emitVerilog(new X(new Mod1))) +println(ChiselStage.emitVerilog(new X(new Mod2))) +``` diff --git a/docs/src/explanations/ports.md b/docs/src/explanations/ports.md new file mode 100644 index 00000000..ce38cf22 --- /dev/null +++ b/docs/src/explanations/ports.md @@ -0,0 +1,67 @@ +--- +layout: docs +title: "Ports" +section: "chisel3" +--- + +# Ports + +Ports are used as interfaces to hardware components. A port is simply +any `Data` object that has directions assigned to its members. + +Chisel provides port constructors to allow a direction to be added +(input or output) to an object at construction time. Primitive port +constructors wrap the type of the port in `Input` or `Output`. + +An example port declaration is as follows: +```scala mdoc:invisible +import chisel3._ +``` +```scala mdoc +class Decoupled extends Bundle { + val ready = Output(Bool()) + val data = Input(UInt(32.W)) + val valid = Input(Bool()) +} +``` + +After defining ```Decoupled```, it becomes a new type that can be +used as needed for module interfaces or for named collections of +wires. + +By folding directions into the object declarations, Chisel is able to +provide powerful wiring constructs described later. + +## Inspecting Module ports + +(Chisel 3.2+) + +Chisel 3.2 introduced `DataMirror.modulePorts` which can be used to inspect the IOs of any Chisel module (this includes modules in both `import chisel3._` and `import Chisel._`, as well as BlackBoxes from each package). +Here is an example of how to use this API: + +```scala mdoc +import chisel3.experimental.DataMirror +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} + +class Adder extends Module { + val a = IO(Input(UInt(8.W))) + val b = IO(Input(UInt(8.W))) + val c = IO(Output(UInt(8.W))) + c := a +& b +} + +class Test extends Module { + val adder = Module(new Adder) + // for debug only + adder.a := DontCare + adder.b := DontCare + + // Inspect ports of adder + // See the result below. + DataMirror.modulePorts(adder).foreach { case (name, port) => { + println(s"Found port $name: $port") + }} +} + +(new ChiselStage).execute(Array.empty, Seq(ChiselGeneratorAnnotation(() => new Test))) +``` \ No newline at end of file diff --git a/docs/src/explanations/printing.md b/docs/src/explanations/printing.md new file mode 100644 index 00000000..abd1a427 --- /dev/null +++ b/docs/src/explanations/printing.md @@ -0,0 +1,137 @@ +--- +layout: docs +title: "Printing" +section: "chisel3" +--- + +# Printing in Chisel + +Chisel provides the `printf` function for debugging purposes. It comes in two flavors: + +* [Scala-style](#scala-style) +* [C-style](#c-style) + +### Scala-style + +Chisel also supports printf in a style similar to [Scala's String Interpolation](http://docs.scala-lang.org/overviews/core/string-interpolation.html). Chisel provides a custom string interpolator `p` which can be used as follows: + +```scala mdoc:invisible +import chisel3._ +``` +```scala mdoc:compile-only +val myUInt = 33.U +printf(p"myUInt = $myUInt") // myUInt = 33 +``` + +Note that when concatenating `p"..."` strings, you need to start with a `p"..."` string: + +```scala mdoc:compile-only +// Does not interpolate the second string +val myUInt = 33.U +printf("my normal string" + p"myUInt = $myUInt") +``` + +#### Simple formatting + +Other formats are available as follows: + +```scala mdoc:compile-only +val myUInt = 33.U +// Hexadecimal +printf(p"myUInt = 0x${Hexadecimal(myUInt)}") // myUInt = 0x21 +// Binary +printf(p"myUInt = ${Binary(myUInt)}") // myUInt = 100001 +// Character +printf(p"myUInt = ${Character(myUInt)}") // myUInt = ! +``` + +We recognize that the format specifiers are verbose, so we are working on a more concise syntax. + +#### Aggregate data-types + +Chisel provides default custom "pretty-printing" for Vecs and Bundles. The default printing of a Vec is similar to printing a Seq or List in Scala while printing a Bundle is similar to printing a Scala Map. + +```scala mdoc:compile-only +val myVec = VecInit(5.U, 10.U, 13.U) +printf(p"myVec = $myVec") // myVec = Vec(5, 10, 13) + +val myBundle = Wire(new Bundle { + val foo = UInt() + val bar = UInt() +}) +myBundle.foo := 3.U +myBundle.bar := 11.U +printf(p"myBundle = $myBundle") // myBundle = Bundle(a -> 3, b -> 11) +``` + +#### Custom Printing + +Chisel also provides the ability to specify _custom_ printing for user-defined Bundles. + +```scala mdoc:compile-only +class Message extends Bundle { + val valid = Bool() + val addr = UInt(32.W) + val length = UInt(4.W) + val data = UInt(64.W) + override def toPrintable: Printable = { + val char = Mux(valid, 'v'.U, '-'.U) + p"Message:\n" + + p" valid : ${Character(char)}\n" + + p" addr : 0x${Hexadecimal(addr)}\n" + + p" length : $length\n" + + p" data : 0x${Hexadecimal(data)}\n" + } +} + +val myMessage = Wire(new Message) +myMessage.valid := true.B +myMessage.addr := "h1234".U +myMessage.length := 10.U +myMessage.data := "hdeadbeef".U + +printf(p"$myMessage") +``` + +Which prints the following: + +``` +Message: + valid : v + addr : 0x00001234 + length : 10 + data : 0x00000000deadbeef +``` + +Notice the use of `+` between `p` interpolated "strings". The results of `p` interpolation can be concatenated by using the `+` operator. For more information, please see the documentation + +### C-Style + +Chisel provides `printf` in a similar style to its C namesake. It accepts a double-quoted format string and a variable number of arguments which will then be printed on rising clock edges. Chisel supports the following format specifiers: + +| Format Specifier | Meaning | +| :-----: | :-----: | +| `%d` | decimal number | +| `%x` | hexadecimal number | +| `%b` | binary number | +| `%c` | 8-bit ASCII character | +| `%%` | literal percent | + +It also supports a small set of escape characters: + +| Escape Character | Meaning | +| :-----: | :-----: | +| `\n` | newline | +| `\t` | tab | +| `\"` | literal double quote | +| `\'` | literal single quote | +| `\\` | literal backslash | + +Note that single quotes do not require escaping, but are legal to escape. + +Thus printf can be used in a way very similar to how it is used in C: + +```scala mdoc:compile-only +val myUInt = 32.U +printf("myUInt = %d", myUInt) // myUInt = 32 +``` diff --git a/docs/src/explanations/reset.md b/docs/src/explanations/reset.md new file mode 100644 index 00000000..a99a39e3 --- /dev/null +++ b/docs/src/explanations/reset.md @@ -0,0 +1,179 @@ +--- +layout: docs +title: "Reset" +section: "chisel3" +--- + +# Reset + +```scala mdoc:invisible +import chisel3._ + +class Submodule extends Module +``` + +As of Chisel 3.2.0, Chisel 3 supports both synchronous and asynchronous reset, +meaning that it can natively emit both synchronous and asynchronously reset registers. + +The type of register that is emitted is based on the type of the reset signal associated +with the register. + +There are three types of reset that implement a common trait `Reset`: +* `Bool` - constructed with `Bool()`. Also known as "synchronous reset". +* `AsyncReset` - constructed with `AsyncReset()`. Also known as "asynchronous reset". +* `Reset` - constructed with `Reset()`. Also known as "abstract reset". + +For implementation reasons, the concrete Scala type is `ResetType`. Stylistically we avoid `ResetType`, instead using the common trait `Reset`. + +Registers with reset signals of type `Bool` are emitted as synchronous reset flops. +Registers with reset signals of type `AsyncReset` are emitted as asynchronouly reset flops. +Registers with reset signals of type `Reset` will have their reset type _inferred_ during FIRRTL compilation. + +### Reset Inference + +FIRRTL will infer a concrete type for any signals of type abstract `Reset`. +The rules are as follows: +1. An abstract `Reset` with only signals of type `AsyncReset`, abstract `Reset`, and `DontCare` +in both its fan-in and fan-out will infer to be of type `AsyncReset` +2. An abstract `Reset` with signals of both types `Bool` and `AsyncReset` in its fan-in and fan-out +is an error. +3. Otherwise, an abstract `Reset` will infer to type `Bool`. + +You can think about (3) as the mirror of (1) replacing `AsyncReset` with `Bool` with the additional +rule that abstract `Reset`s with neither `AsyncReset` nor `Bool` in their fan-in and fan-out will +default to type `Bool`. +This "default" case is uncommon and implies that reset signal is ultimately driven by a `DontCare`. + +### Implicit Reset + +A `Module`'s `reset` is of type abstract `Reset`. +Prior to Chisel 3.2.0, the type of this field was `Bool`. +For backwards compatability, if the top-level module has an implicit reset, its type will default to `Bool`. + +#### Setting Implicit Reset Type + +_New in Chisel 3.3.0_ + +If you would like to set the reset type from within a Module (including the top-level `Module`), +rather than relying on _Reset Inference_, you can mixin one of the following traits: +* `RequireSyncReset` - sets the type of `reset` to `Bool` +* `RequireAsyncReset` - sets the type of `reset` to `AsyncReset` + +For example: + +```scala mdoc:silent +class MyAlwaysSyncResetModule extends Module with RequireSyncReset { + val mySyncResetReg = RegInit(false.B) // reset is of type Bool +} +``` + +```scala mdoc:silent +class MyAlwaysAsyncResetModule extends Module with RequireAsyncReset { + val myAsyncResetReg = RegInit(false.B) // reset is of type AsyncReset +} +``` + +**Note:** This sets the concrete type, but the Scala type will remain `Reset`, so casting may still be necessary. +This comes up most often when using a reset of type `Bool` in logic. + + +### Reset-Agnostic Code + +The purpose of abstract `Reset` is to make it possible to design hardware that is agnostic to the +reset discipline used. +This enables code reuse for utilities and designs where the reset discipline does not matter to +the functionality of the block. + +Consider the two example modules below which are agnostic to the type of reset used within them: + +```scala mdoc:silent +class ResetAgnosticModule extends Module { + val io = IO(new Bundle { + val out = UInt(4.W) + }) + val resetAgnosticReg = RegInit(0.U(4.W)) + resetAgnosticReg := resetAgnosticReg + 1.U + io.out := resetAgnosticReg +} + +class ResetAgnosticRawModule extends RawModule { + val clk = IO(Input(Clock())) + val rst = IO(Input(Reset())) + val out = IO(Output(UInt(8.W))) + + val resetAgnosticReg = withClockAndReset(clk, rst)(RegInit(0.U(8.W))) + resetAgnosticReg := resetAgnosticReg + 1.U + out := resetAgnosticReg +} +``` + +These modules can be used in both synchronous and asynchronous reset domains. +Their reset types will be inferred based on the context within which they are used. + +### Forcing Reset Type + +You can set the type of a Module's implicit reset as described [above](#setting-implicit-reset-type). + +You can also cast to force the concrete type of reset. +* `.asBool` will reinterpret a `Reset` as `Bool` +* `.asAsyncReset` will reinterpret a `Reset` as `AsyncReset`. + +You can then use `withReset` to use a cast reset as the implicit reset. +See ["Multiple Clock Domains"](../explanations/multi-clock) for more information about `withReset`. + + +The following will make `myReg` as well as both `resetAgnosticReg`s synchronously reset: + +```scala mdoc:silent +class ForcedSyncReset extends Module { + // withReset's argument becomes the implicit reset in its scope + withReset (reset.asBool) { + val myReg = RegInit(0.U) + val myModule = Module(new ResetAgnosticModule) + + // RawModules do not have implicit resets so withReset has no effect + val myRawModule = Module(new ResetAgnosticRawModule) + // We must drive the reset port manually + myRawModule.rst := Module.reset // Module.reset grabs the current implicit reset + } +} +``` + +The following will make `myReg` as well as both `resetAgnosticReg`s asynchronously reset: + +```scala mdoc:silent +class ForcedAysncReset extends Module { + // withReset's argument becomes the implicit reset in its scope + withReset (reset.asAsyncReset){ + val myReg = RegInit(0.U) + val myModule = Module(new ResetAgnosticModule) // myModule.reset is connected implicitly + + // RawModules do not have implicit resets so withReset has no effect + val myRawModule = Module(new ResetAgnosticRawModule) + // We must drive the reset port manually + myRawModule.rst := Module.reset // Module.reset grabs the current implicit reset + } +} +``` + +**Note:** such casts (`asBool` and `asAsyncReset`) are not checked by FIRRTL. +In doing such a cast, you as the designer are effectively telling the compiler +that you know what you are doing and to force the type as cast. + +### Last-Connect Semantics + +It is **not** legal to override the reset type using last-connect semantics +unless you are overriding a `DontCare`: + +```scala mdoc:silent +class MyModule extends Module { + val resetBool = Wire(Reset()) + resetBool := DontCare + resetBool := false.B // this is fine + withReset(resetBool) { + val mySubmodule = Module(new Submodule()) + } + resetBool := true.B // this is fine + resetBool := false.B.asAsyncReset // this will error in FIRRTL +} +``` diff --git a/docs/src/explanations/sequential-circuits.md b/docs/src/explanations/sequential-circuits.md new file mode 100644 index 00000000..938416ac --- /dev/null +++ b/docs/src/explanations/sequential-circuits.md @@ -0,0 +1,50 @@ +--- +layout: docs +title: "Sequential Circuits" +section: "chisel3" +--- + +# Sequential Circuits + +```scala mdoc:invisible +import chisel3._ +val in = Bool() +``` +The simplest form of state element supported by Chisel is a positive edge-triggered register, which can be instantiated as: +``` scala mdoc:compile-only +val reg = RegNext(in) +``` +This circuit has an output that is a copy of the input signal `in` delayed by one clock cycle. Note that we do not have to specify the type of Reg as it will be automatically inferred from its input when instantiated in this way. In the current version of Chisel, clock and reset are global signals that are implicitly included where needed. + +Note that registers which do not specify an initial value will not change value upon toggling the reset signal. + +Using registers, we can quickly define a number of useful circuit constructs. For example, a rising-edge detector that takes a boolean signal in and outputs true when the current value is true and the previous value is false is given by: + +```scala mdoc:silent +def risingedge(x: Bool) = x && !RegNext(x) +``` +Counters are an important sequential circuit. To construct an up-counter that counts up to a maximum value, max, then wraps around back to zero (i.e., modulo max+1), we write: +```scala mdoc:silent +def counter(max: UInt) = { + val x = RegInit(0.asUInt(max.getWidth.W)) + x := Mux(x === max, 0.U, x + 1.U) + x +} +``` +The counter register is created in the counter function with a reset value of 0 (with width large enough to hold max), to which the register will be initialized when the global reset for the circuit is asserted. The := assignment to x in counter wires an update combinational circuit which increments the counter value unless it hits the max at which point it wraps back to zero. Note that when x appears on the right-hand side of an assignment, its output is referenced, whereas when on the left-hand side, its input is referenced. +Counters can be used to build a number of useful sequential circuits. For example, we can build a pulse generator by outputting true when a counter reaches zero: +```scala mdoc:silent +// Produce pulse every n cycles. +def pulse(n: UInt) = counter(n - 1.U) === 0.U +``` +A square-wave generator can then be toggled by the pulse train, toggling between true and false on each pulse: +```scala mdoc:silent +// Flip internal state when input true. +def toggle(p: Bool) = { + val x = RegInit(false.B) + x := Mux(p, !x, x) + x +} +// Square wave of a given period. +def squareWave(period: UInt) = toggle(pulse(period >> 1)) +``` diff --git a/docs/src/explanations/supported-hardware.md b/docs/src/explanations/supported-hardware.md new file mode 100644 index 00000000..fe3909e7 --- /dev/null +++ b/docs/src/explanations/supported-hardware.md @@ -0,0 +1,11 @@ +--- +layout: docs +title: "Supported Hardware" +section: "chisel3" +--- + +# Supported Hardware + +While Chisel focuses on binary logic, Chisel can support analog and tri-state wires with the `Analog` type - see [Datatypes in Chisel](data-types). + +We focus on binary logic designs as they constitute the vast majority of designs in practice. Tri-state logic are poorly supported standard industry flows and require special/controlled hard macros in order to be done. diff --git a/docs/src/explanations/unconnected-wires.md b/docs/src/explanations/unconnected-wires.md new file mode 100644 index 00000000..48012d12 --- /dev/null +++ b/docs/src/explanations/unconnected-wires.md @@ -0,0 +1,138 @@ +--- +layout: docs +title: "Unconnected Wires" +section: "chisel3" +--- + +# Unconnected Wires + +The Invalidate API [(#645)](https://github.com/freechipsproject/chisel3/pull/645) adds support to Chisel +for reporting unconnected wires as errors. + +Prior to this pull request, Chisel automatically generated a firrtl `is invalid` for `Module IO()`, and each `Wire()` definition. +This made it difficult to detect cases where output signals were never driven. +Chisel now supports a `DontCare` element, which may be connected to an output signal, indicating that that signal is intentionally not driven. +Unless a signal is driven by hardware or connected to a `DontCare`, Firrtl will complain with a "not fully initialized" error. + +### API + +Output signals may be connected to DontCare, generating a `is invalid` when the corresponding firrtl is emitted. + +```scala mdoc:invisible +import chisel3._ +``` +```scala mdoc:silent + +class Out extends Bundle { + val debug = Bool() + val debugOption = Bool() +} +val io = new Bundle { val out = new Out } +``` + +```scala mdoc:compile-only +io.out.debug := true.B +io.out.debugOption := DontCare +``` + +This indicates that the signal `io.out.debugOption` is intentionally not driven and firrtl should not issue a "not fully initialized" +error for this signal. + +This can be applied to aggregates as well as individual signals: +```scala mdoc:invisible +import chisel3._ +``` +```scala mdoc:silent +import chisel3._ +class ModWithVec extends Module { + // ... + val nElements = 5 + val io = IO(new Bundle { + val outs = Output(Vec(nElements, Bool())) + }) + io.outs <> DontCare + // ... +} + +class TrivialInterface extends Bundle { + val in = Input(Bool()) + val out = Output(Bool()) +} + +class ModWithTrivalInterface extends Module { + // ... + val io = IO(new TrivialInterface) + io <> DontCare + // ... +} +``` + +This feature is controlled by `CompileOptions.explicitInvalidate` and is set to `false` in `NotStrict` (Chisel2 compatibility mode), +and `true` in `Strict` mode. + +You can selectively enable this for Chisel2 compatibility mode by providing your own explicit `compileOptions`, +either for a group of Modules (via inheritance): +```scala mdoc:silent +abstract class ExplicitInvalidateModule extends Module()(chisel3.ExplicitCompileOptions.NotStrict.copy(explicitInvalidate = true)) +``` +or on a per-Module basis: +```scala mdoc:silent +class MyModule extends Module { + override val compileOptions = chisel3.ExplicitCompileOptions.NotStrict.copy(explicitInvalidate = true) + val io = IO(new Bundle { /* ... */ } ) + // ... +} +``` + +Or conversely, disable this stricter checking (which is now the default in pure chisel3): +```scala mdoc:silent +abstract class ImplicitInvalidateModule extends Module()(chisel3.ExplicitCompileOptions.Strict.copy(explicitInvalidate = false)) +``` +or on a per-Module basis: +```scala mdoc:invisible:reset +import chisel3._ +``` +```scala mdoc:silent +class MyModule extends Module { + override val compileOptions = chisel3.ExplicitCompileOptions.Strict.copy(explicitInvalidate = false) + val io = IO(new Bundle { /* ... */ } ) + // ... +} +``` + +Please see the corresponding [API tests](https://github.com/freechipsproject/chisel3/blob/master/src/test/scala/chiselTests/InvalidateAPISpec.scala) +for examples. + +### Determining the unconnected element + +I have an interface with 42 wires. +Which one of them is unconnected? + +The firrtl error message should contain something like: +```bash +firrtl.passes.CheckInitialization$RefNotInitializedException: @[:@6.4] : [module Router] Reference io is not fully initialized. + @[Decoupled.scala 38:19:@48.12] : node _GEN_23 = mux(and(UInt<1>("h1"), eq(UInt<2>("h3"), _T_84)), _GEN_2, VOID) @[Decoupled.scala 38:19:@48.12] + @[Router.scala 78:30:@44.10] : node _GEN_36 = mux(_GEN_0.ready, _GEN_23, VOID) @[Router.scala 78:30:@44.10] + @[Router.scala 75:26:@39.8] : node _GEN_54 = mux(io.in.valid, _GEN_36, VOID) @[Router.scala 75:26:@39.8] + @[Router.scala 70:50:@27.6] : node _GEN_76 = mux(io.load_routing_table_request.valid, VOID, _GEN_54) @[Router.scala 70:50:@27.6] + @[Router.scala 65:85:@19.4] : node _GEN_102 = mux(_T_62, VOID, _GEN_76) @[Router.scala 65:85:@19.4] + : io.outs[3].bits.body <= _GEN_102 +``` +The first line is the initial error report. +Successive lines, indented and beginning with source line information indicate connections involving the problematic signal. +Unfortunately, if these are `when` conditions involving muxes, they may be difficult to decipher. +The last line of the group, indented and beginning with a `:` should indicate the uninitialized signal component. +This example (from the [Router tutorial](https://github.com/ucb-bar/chisel-tutorial/blob/release/src/main/scala/examples/Router.scala)) +was produced when the output queue bits were not initialized. +The old code was: +```scala + io.outs.foreach { out => out.noenq() } +``` +which initialized the queue's `valid` bit, but did not initialize the actual output values. +The fix was: +```scala + io.outs.foreach { out => + out.bits := 0.U.asTypeOf(out.bits) + out.noenq() + } +``` diff --git a/docs/src/explanations/width-inference.md b/docs/src/explanations/width-inference.md new file mode 100644 index 00000000..66d9736d --- /dev/null +++ b/docs/src/explanations/width-inference.md @@ -0,0 +1,40 @@ +--- +layout: docs +title: "Width Inference" +section: "chisel3" +--- + +# Width Inference + +Chisel provides bit width inference to reduce design effort. Users are encouraged to manually specify widths of ports and registers to prevent any surprises, but otherwise unspecified widths will be inferred by the Firrtl compiler. + +For all circuit components declared with unspecified widths, the FIRRTL compiler will infer the minimum possible width that maintains the legality of all its incoming connections. Implicit here is that inference is done in a right to left fashion in the sense of an assignment statement in chisel, i.e. from the left hand side from the right hand side. If a component has no incoming connections, and the width is unspecified, then an error is thrown to indicate that the width could not be inferred. + +For module input ports with unspecified widths, the inferred width is the minimum possible width that maintains the legality of all incoming connections to all instantiations of the module. +The width of a ground-typed multiplexor expression is the maximum of its two corresponding input widths. For multiplexing aggregate-typed expressions, the resulting widths of each leaf subelement is the maximum of its corresponding two input leaf subelement widths. +The width of a conditionally valid expression is the width of its input expression. For the full formal description see the [Firrtl Spec](https://github.com/freechipsproject/firrtl/blob/master/spec/spec.pdf). + +Hardware operators have output widths as defined by the following set of rules: + +| operation | bit width | +| --------- | --------- | +| `z = x + y` *or* `z = x +% y` | `w(z) = max(w(x), w(y))` | +| `z = x +& y` | `w(z) = max(w(x), w(y)) + 1` | +| `z = x - y` *or* `z = x -% y` | `w(z) = max(w(x), w(y))` | +| `z = x -& y` | `w(z) = max(w(x), w(y)) + 1` | +| `z = x & y` | `w(z) = max(w(x), w(y))` | +| `z = Mux(c, x, y)` | `w(z) = max(w(x), w(y))` | +| `z = w * y` | `w(z) = w(x) + w(y)` | +| `z = x << n` | `w(z) = w(x) + maxNum(n)` | +| `z = x >> n` | `w(z) = w(x) - minNum(n)` | +| `z = Cat(x, y)` | `w(z) = w(x) + w(y)` | +| `z = Fill(n, x)` | `w(z) = w(x) * maxNum(n)` | + +>where for instance `w(z)` is the bit width of wire `z`, and the `&` +rule applies to all bitwise logical operations. + +Given a path of connections that begins with an unspecified width element (most commonly a top-level input), then the compiler will throw an exception indicating a certain width was uninferrable. + +A common "gotcha" comes from truncating addition and subtraction with the operators `+` and `-`. Users who want the result to maintain the full, expanded precision of the addition or subtraction should use the expanding operators `+&` and `-&`. + +> The default truncating operation comes from Chisel's history as a microprocessor design language. -- cgit v1.2.3