Skip to content

Commit 0d73982

Browse files
authored
Aggregate PriorityMux and Mux1H Seq size errors (#4609)
This requires adding source locators to PriorityMux and Mux1H which requires SourceInfoTransform macros in Scala 2 and thus splitting the Scala 2 / Scala 3 public interfaces. Because macro applications do not support named arguments in Scala 2, this is an API change.
1 parent 5d68bea commit 0d73982

File tree

6 files changed

+163
-50
lines changed

6 files changed

+163
-50
lines changed

macros/src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ class SourceInfoTransform(val c: Context) extends AutoSourceTransform {
324324
def inNEnUseDualPortSramNameArg(in: c.Tree, n: c.Tree, en: c.Tree, useDualPortSram: c.Tree, name: c.Tree): c.Tree = {
325325
q"$thisObj.$doFuncTerm($in, $n, $en, $useDualPortSram, $name)($implicitSourceInfo)"
326326
}
327+
328+
def selInArg(sel: c.Tree, in: c.Tree): c.Tree = {
329+
q"$thisObj.$doFuncTerm($sel, $in)($implicitSourceInfo)"
330+
}
327331
}
328332

329333
// Workaround for https://github.com/sbt/sbt/issues/3966

src/main/scala-2/chisel3/util/Mux.scala

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,63 @@ package chisel3.util
77

88
import chisel3._
99
import chisel3.experimental.SourceInfo
10-
import chisel3.internal.sourceinfo.MuxLookupTransform
10+
import chisel3.internal.sourceinfo.{MuxLookupTransform, SourceInfoTransform}
1111
import scala.language.experimental.macros
1212

13+
/** Builds a Mux tree out of the input signal vector using a one hot encoded
14+
* select signal. Returns the output of the Mux tree.
15+
*
16+
* @example {{{
17+
* val hotValue = chisel3.util.Mux1H(Seq(
18+
* io.selector(0) -> 2.U,
19+
* io.selector(1) -> 4.U,
20+
* io.selector(2) -> 8.U,
21+
* io.selector(4) -> 11.U,
22+
* ))
23+
* }}}
24+
*
25+
* @note results unspecified unless exactly one select signal is high
26+
*/
27+
object Mux1H extends Mux1HImpl {
28+
def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = macro SourceInfoTransform.selInArg
29+
def do_apply[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
30+
31+
def apply[T <: Data](in: Iterable[(Bool, T)]): T = macro SourceInfoTransform.inArg
32+
def do_apply[T <: Data](in: Iterable[(Bool, T)])(implicit sourceInfo: SourceInfo): T = _applyImpl(in)
33+
34+
def apply[T <: Data](sel: UInt, in: Seq[T]): T = macro SourceInfoTransform.selInArg
35+
def do_apply[T <: Data](sel: UInt, in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
36+
37+
def apply(sel: UInt, in: UInt): Bool = macro SourceInfoTransform.selInArg
38+
def do_apply(sel: UInt, in: UInt)(implicit sourceInfo: SourceInfo): Bool = _applyImpl(sel, in)
39+
40+
}
41+
42+
/** Builds a Mux tree under the assumption that multiple select signals
43+
* can be enabled. Priority is given to the first select signal.
44+
*
45+
* @example {{{
46+
* val hotValue = chisel3.util.PriorityMux(Seq(
47+
* io.selector(0) -> 2.U,
48+
* io.selector(1) -> 4.U,
49+
* io.selector(2) -> 8.U,
50+
* io.selector(4) -> 11.U,
51+
* ))
52+
* }}}
53+
* Returns the output of the Mux tree.
54+
*/
55+
object PriorityMux extends PriorityMuxImpl {
56+
57+
def apply[T <: Data](in: Seq[(Bool, T)]): T = macro SourceInfoTransform.inArg
58+
def do_apply[T <: Data](in: Seq[(Bool, T)])(implicit sourceInfo: SourceInfo): T = _applyImpl(in)
59+
60+
def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = macro SourceInfoTransform.selInArg
61+
def do_apply[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
62+
63+
def apply[T <: Data](sel: Bits, in: Seq[T]): T = macro SourceInfoTransform.selInArg
64+
def do_apply[T <: Data](sel: Bits, in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
65+
}
66+
1367
/** Creates a cascade of n Muxs to search for a key value. The Selector may be a UInt or an EnumType.
1468
*
1569
* @example {{{

src/main/scala-3/chisel3/util/Mux.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,53 @@ package chisel3.util
88
import chisel3._
99
import chisel3.experimental.SourceInfo
1010

11+
/** Builds a Mux tree out of the input signal vector using a one hot encoded
12+
* select signal. Returns the output of the Mux tree.
13+
*
14+
* @example {{{
15+
* val hotValue = chisel3.util.Mux1H(Seq(
16+
* io.selector(0) -> 2.U,
17+
* io.selector(1) -> 4.U,
18+
* io.selector(2) -> 8.U,
19+
* io.selector(4) -> 11.U,
20+
* ))
21+
* }}}
22+
*
23+
* @note results unspecified unless exactly one select signal is high
24+
*/
25+
object Mux1H extends Mux1HImpl {
26+
27+
def apply[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
28+
29+
def apply[T <: Data](in: Iterable[(Bool, T)])(implicit sourceInfo: SourceInfo): T = _applyImpl(in)
30+
31+
def apply[T <: Data](sel: UInt, in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
32+
33+
def apply(sel: UInt, in: UInt)(implicit sourceInfo: SourceInfo): Bool = _applyImpl(sel, in)
34+
}
35+
36+
/** Builds a Mux tree under the assumption that multiple select signals
37+
* can be enabled. Priority is given to the first select signal.
38+
*
39+
* @example {{{
40+
* val hotValue = chisel3.util.PriorityMux(Seq(
41+
* io.selector(0) -> 2.U,
42+
* io.selector(1) -> 4.U,
43+
* io.selector(2) -> 8.U,
44+
* io.selector(4) -> 11.U,
45+
* ))
46+
* }}}
47+
* Returns the output of the Mux tree.
48+
*/
49+
object PriorityMux extends PriorityMuxImpl {
50+
51+
def apply[T <: Data](in: Seq[(Bool, T)])(implicit sourceInfo: SourceInfo): T = _applyImpl(in)
52+
53+
def apply[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
54+
55+
def apply[T <: Data](sel: Bits, in: Seq[T])(implicit sourceInfo: SourceInfo): T = _applyImpl(sel, in)
56+
}
57+
1158
/** Creates a cascade of n Muxs to search for a key value. The Selector may be a UInt or an EnumType.
1259
*
1360
* @example {{{

src/main/scala/chisel3/util/MuxImpl.scala

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,38 @@ package chisel3.util
77

88
import chisel3._
99
import chisel3.experimental.SourceInfo
10+
import chisel3.internal.Builder
1011

11-
/** Builds a Mux tree out of the input signal vector using a one hot encoded
12-
* select signal. Returns the output of the Mux tree.
13-
*
14-
* @example {{{
15-
* val hotValue = chisel3.util.Mux1H(Seq(
16-
* io.selector(0) -> 2.U,
17-
* io.selector(1) -> 4.U,
18-
* io.selector(2) -> 8.U,
19-
* io.selector(4) -> 11.U,
20-
* ))
21-
* }}}
22-
*
23-
* @note results unspecified unless exactly one select signal is high
24-
*/
25-
object Mux1H {
26-
def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = {
27-
require(sel.size == in.size, s"Mux1H: input Seqs must have the same length, got sel ${sel.size} and in ${in.size}")
28-
apply(sel.zip(in))
12+
private[chisel3] trait Mux1HImpl {
13+
protected def _applyImpl[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = {
14+
if (sel.size != in.size) {
15+
Builder.error(s"Mux1H: input Seqs must have the same length, got sel ${sel.size} and in ${in.size}")
16+
}
17+
_applyImpl(sel.zip(in))
2918
}
30-
def apply[T <: Data](in: Iterable[(Bool, T)]): T = SeqUtils.oneHotMux(in)
31-
def apply[T <: Data](sel: UInt, in: Seq[T]): T =
32-
apply((0 until in.size).map(sel(_)), in)
33-
def apply(sel: UInt, in: UInt): Bool = (sel & in).orR
19+
20+
protected def _applyImpl[T <: Data](in: Iterable[(Bool, T)])(implicit sourceInfo: SourceInfo): T =
21+
SeqUtils.oneHotMux(in)
22+
23+
protected def _applyImpl[T <: Data](sel: UInt, in: Seq[T])(implicit sourceInfo: SourceInfo): T =
24+
_applyImpl((0 until in.size).map(sel(_)), in)
25+
26+
protected def _applyImpl(sel: UInt, in: UInt)(implicit sourceInfo: SourceInfo): Bool = (sel & in).orR
3427
}
3528

36-
/** Builds a Mux tree under the assumption that multiple select signals
37-
* can be enabled. Priority is given to the first select signal.
38-
*
39-
* @example {{{
40-
* val hotValue = chisel3.util.PriorityMux(Seq(
41-
* io.selector(0) -> 2.U,
42-
* io.selector(1) -> 4.U,
43-
* io.selector(2) -> 8.U,
44-
* io.selector(4) -> 11.U,
45-
* ))
46-
* }}}
47-
* Returns the output of the Mux tree.
48-
*/
49-
object PriorityMux {
50-
def apply[T <: Data](in: Seq[(Bool, T)]): T = SeqUtils.priorityMux(in)
51-
def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = {
52-
require(
53-
sel.size == in.size,
54-
s"PriorityMux: input Seqs must have the same length, got sel ${sel.size} and in ${in.size}"
55-
)
56-
apply(sel.zip(in))
29+
private[chisel3] trait PriorityMuxImpl {
30+
31+
protected def _applyImpl[T <: Data](in: Seq[(Bool, T)]): T = SeqUtils.priorityMux(in)
32+
33+
protected def _applyImpl[T <: Data](sel: Seq[Bool], in: Seq[T])(implicit sourceInfo: SourceInfo): T = {
34+
if (sel.size != in.size) {
35+
Builder.error(s"PriorityMux: input Seqs must have the same length, got sel ${sel.size} and in ${in.size}")
36+
}
37+
_applyImpl(sel.zip(in))
5738
}
58-
def apply[T <: Data](sel: Bits, in: Seq[T]): T = apply((0 until in.size).map(sel(_)), in)
39+
40+
protected def _applyImpl[T <: Data](sel: Bits, in: Seq[T])(implicit sourceInfo: SourceInfo): T =
41+
_applyImpl((0 until in.size).map(sel(_)), in)
5942
}
6043

6144
private[chisel3] trait MuxLookupImpl {

src/test/scala/chiselTests/OneHotMuxSpec.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package chiselTests
55
import chisel3._
66
import chisel3.testers.BasicTester
77
import chisel3.util.{Mux1H, UIntToOH}
8+
import _root_.circt.stage.ChiselStage.emitCHIRRTL
89
import org.scalatest._
910
import org.scalatest.freespec.AnyFreeSpec
1011
import org.scalatest.matchers.should.Matchers
@@ -32,12 +33,23 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners {
3233
e.getMessage should include("Mux1H must have a non-empty argument")
3334
}
3435
"Mux1H should give a error when given different size Seqs" in {
35-
val e = intercept[IllegalArgumentException] {
36-
Mux1H(Seq(true.B, true.B), Seq(1.U, 2.U, 3.U))
36+
val e = intercept[ChiselException] {
37+
emitCHIRRTL(
38+
new RawModule {
39+
Mux1H(Seq(true.B, false.B), Seq(1.U, 2.U, 3.U))
40+
},
41+
args = Array("--throw-on-first-error")
42+
)
3743
}
44+
e.getMessage should include("OneHotMuxSpec.scala") // Make sure source locator comes from this file
3845
e.getMessage should include("Mux1H: input Seqs must have the same length, got sel 2 and in 3")
3946
}
40-
47+
// The input bitvector is sign extended to the width of the sequence
48+
"Mux1H should NOT error when given mismatched selector width and Seq size" in {
49+
emitCHIRRTL(new RawModule {
50+
Mux1H("b10".U(2.W), Seq(1.U, 2.U, 3.U))
51+
})
52+
}
4153
}
4254

4355
class SimpleOneHotTester extends BasicTester {

src/test/scala/chiselTests/util/PriorityMuxSpec.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,22 @@ class PriorityMuxSpec extends ChiselFlatSpec {
6565
}
6666

6767
it should "give a error when given different size Seqs" in {
68-
val e = intercept[IllegalArgumentException] {
69-
PriorityMux(Seq(true.B, true.B), Seq(1.U, 2.U, 3.U))
68+
val e = intercept[ChiselException] {
69+
emitCHIRRTL(
70+
new RawModule {
71+
PriorityMux(Seq(true.B, false.B), Seq(1.U, 2.U, 3.U))
72+
},
73+
args = Array("--throw-on-first-error")
74+
)
7075
}
76+
e.getMessage should include("PriorityMuxSpec.scala") // Make sure source locator comes from this file
7177
e.getMessage should include("PriorityMux: input Seqs must have the same length, got sel 2 and in 3")
7278
}
79+
80+
// The input bitvector is sign extended to the width of the sequence
81+
it should "NOT error when given mismatched selector width and Seq size" in {
82+
emitCHIRRTL(new RawModule {
83+
PriorityMux("b10".U(2.W), Seq(1.U, 2.U, 3.U))
84+
})
85+
}
7386
}

0 commit comments

Comments
 (0)