Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print warning when extractor is too wide or narrow #3033

Merged
16 changes: 15 additions & 1 deletion core/src/main/scala/chisel3/Bits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,22 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi

/** @group SourceInfoTransformMacro */
final def do_extract(x: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
this.widthOption.foreach { thisWidth =>
if (thisWidth == 0) {
Builder.error(s"Cannot extract from zero-width")
albertchen-sifive marked this conversation as resolved.
Show resolved Hide resolved
} else {
x.widthOption.foreach { xWidth =>
if (xWidth >= 31 || (1 << (xWidth - 1)) >= thisWidth) {
Builder.warning(s"Dynamic index with width $xWidth is too large for extractee of width $thisWidth")
albertchen-sifive marked this conversation as resolved.
Show resolved Hide resolved
} else if ((1 << xWidth) < thisWidth) {
Builder.warning(s"Dynamic index with width $xWidth is too small for extractee of width $thisWidth")
albertchen-sifive marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
val theBits = this >> x
theBits(0)
val noExtract = theBits.widthOption.exists(_ <= 1)
if (noExtract) theBits.asBool else theBits(0)
}

/** Returns the specified bit on this wire as a [[Bool]], dynamically addressed.
Expand Down
32 changes: 32 additions & 0 deletions docs/src/cookbooks/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Please note that these examples make use of [Chisel's scala-style printing](../e
* [How do I create an optional I/O?](#how-do-i-create-an-optional-io)
* [How do I create I/O without a prefix?](#how-do-i-create-io-without-a-prefix)
* [How do I minimize the number of bits used in an output vector](#how-do-i-minimize-the-number-of-bits-used-in-an-output-vector)
* [How do I resolve `Dynamic index ... is too wide/narrow for extractee ...`?](#how-do-i-resolve-dynamic-index--is-too-wide-narrow-for-extractee)
* Predictable Naming
* [How do I get Chisel to name signals properly in blocks like when/withClockAndReset?](#how-do-i-get-chisel-to-name-signals-properly-in-blocks-like-whenwithclockandreset)
* [How do I get Chisel to name the results of vector reads properly?](#how-do-i-get-chisel-to-name-the-results-of-vector-reads-properly)
Expand Down Expand Up @@ -777,6 +778,37 @@ circt.stage.ChiselStage.emitSystemVerilog(new CountBits(4))
.head + ");\n"
```

### How do I resolve `Dynamic index ... is too wide/narrow for extractee ...`?

If the index is too narrow you can use `.pad` to increase the width.
```scala mdoc:silent
import chisel3.util.log2Up

class TooNarrow(extracteeWidth: Int, indexWidth: Int) {
val extractee = Wire(UInt(extracteeWidth.W))
val index = Wire(UInt(indexWidth.W))
extractee(index.pad(log2Up(extracteeWidth)))
}
```

If the index is too wide you can use a bit extract to select the correct bits.
```scala mdoc:silent
class TooWide(extracteeWidth: Int, indexWidth: Int) {
val extractee = Wire(UInt(extracteeWidth.W))
val index = Wire(UInt(indexWidth.W))
extractee(index(log2Up(extracteeWidth) - 1, 0))
}
```

Or use both if you are working on a generator where the widths may be too wide or too narrow under different circumstances.
```scala mdoc:silent
class TooWideOrNarrow(extracteeWidth: Int, indexWidth: Int) {
val extractee = Wire(UInt(extracteeWidth.W))
val index = Wire(UInt(indexWidth.W))
extractee(index.pad(log2Up(indexWidth))(log2Up(extracteeWidth) - 1, 0))
}
```

## Predictable Naming

### How do I get Chisel to name signals properly in blocks like when/withClockAndReset?
Expand Down
56 changes: 56 additions & 0 deletions src/test/scala/chiselTests/UIntOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,60 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils {
WireDefault(chiselTypeOf(op), op)
}
}

property("emit warning if dynamic index is too wide or too narrow") {
class TooWide extends Module {
val in = IO(Input(UInt(2.W)))
val index = IO(Input(UInt(2.W)))
val out = IO(Output(Bool()))
out := in(index)
}
class TooNarrow extends Module {
val in = IO(Input(UInt(3.W)))
val index = IO(Input(UInt(1.W)))
val out = IO(Output(Bool()))
out := in(index)
}

Seq(
grabLog(ChiselStage.elaborate(new TooWide)),
grabLog(ChiselStage.elaborate(new TooNarrow))
).foreach {
case (log, _) =>
log should include("warn")
}

a[ChiselException] should be thrownBy extractCause[ChiselException] {
ChiselStage.elaborate(new RawModule {
val in = IO(Input(UInt(0.W)))
val index = IO(Input(UInt(1.W)))
val out = IO(Output(Bool()))
out := in(index)
})
}

class Ok extends Module {
val in1 = IO(Input(UInt(9.W)))
val index1 = IO(Input(UInt(4.W)))
val out1 = IO(Output(Bool()))
out1 := in1(index1)

val in2 = IO(Input(UInt(8.W)))
val index2 = IO(Input(UInt(3.W)))
val out2 = IO(Output(Bool()))
out2 := in2(index2)

val in3 = IO(Input(Bool()))
val index3 = IO(Input(UInt(0.W)))
val out3 = IO(Output(Bool()))
out3 := in3(index3)
}

Seq(
grabLog(ChiselStage.elaborate(new Ok))
).foreach {
case (log, _) =>
log should be("")
}
}
}