summaryrefslogtreecommitdiff
path: root/docs/src/explanations/unconnected-wires.md
blob: 48012d12d127817d6f5b71765b9a90202f4160d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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()
  }
```