diff options
| -rw-r--r-- | .github/workflows/install-espresso/action.yml | 25 | ||||
| -rw-r--r-- | .github/workflows/setup-oss-cad-suite/action.yml | 28 | ||||
| -rw-r--r-- | .github/workflows/test.yml | 77 | ||||
| -rw-r--r-- | build.sbt | 1 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Data.scala | 9 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/IO.scala | 41 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Module.scala | 38 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/SeqUtils.scala | 5 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/experimental/package.scala | 2 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Builder.scala | 63 | ||||
| -rw-r--r-- | src/main/scala/chisel3/util/experimental/decode/decoder.scala | 6 | ||||
| -rw-r--r-- | src/test/scala/chisel3/internal/NamespaceSpec.scala | 81 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/RecordSpec.scala | 20 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/experimental/FlatIOSpec.scala | 8 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/util/BitSetSpec.scala | 29 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/util/PriorityMuxSpec.scala | 60 |
16 files changed, 394 insertions, 99 deletions
diff --git a/.github/workflows/install-espresso/action.yml b/.github/workflows/install-espresso/action.yml new file mode 100644 index 00000000..fb1d655a --- /dev/null +++ b/.github/workflows/install-espresso/action.yml @@ -0,0 +1,25 @@ +name: Install Espresso + +inputs: + version: + description: 'version to install' + required: false + default: '2.4' + +runs: + using: composite + steps: + - id: cache-espresso + uses: actions/cache@v3 + with: + path: /usr/local/bin/espresso + key: espresso-${{ runner.os }}-${{ inputs.version }} + + - shell: bash + if: steps.cache-espresso.outputs.cache-hit != 'true' + run: | + cd /tmp + wget -q https://github.com/chipsalliance/espresso/releases/download/v${{ inputs.version }}/x86_64-linux-gnu-espresso + chmod +x x86_64-linux-gnu-espresso + sudo mv x86_64-linux-gnu-espresso /usr/local/bin/espresso + espresso || true diff --git a/.github/workflows/setup-oss-cad-suite/action.yml b/.github/workflows/setup-oss-cad-suite/action.yml new file mode 100644 index 00000000..63d70e92 --- /dev/null +++ b/.github/workflows/setup-oss-cad-suite/action.yml @@ -0,0 +1,28 @@ +name: Setup OSS CAD Suite + +inputs: + version: + description: 'version to install' + required: false + default: '2021-11-09' + +runs: + using: composite + steps: + - id: cache-oss-cad-suite + uses: actions/cache@v3 + with: + path: oss-cad-suite + key: oss-cad-suite-${{ runner.os }}-${{ inputs.version }} + + - shell: bash + if: steps.cache-oss-cad-suite.outputs.cache-hit != 'true' + run: | + VERSION=${{ inputs.version }} + ARTIFACT=oss-cad-suite-linux-x64-$(echo $VERSION | tr -d '-') + wget -q -O - https://github.com/YosysHQ/oss-cad-suite-build/releases/download/${VERSION}/${ARTIFACT}.tgz | tar -zx + + # Add the CAD Suite to the PATH + - shell: bash + run: echo "$(pwd)/oss-cad-suite/bin" >> $GITHUB_PATH + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69426662..dd129812 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,34 +16,26 @@ jobs: strategy: matrix: system: ["ubuntu-20.04"] - jvm: ["adopt@1.8"] + jvm: ["8"] scala: ["2.13.10", "2.12.17"] espresso: ["2.4"] runs-on: ${{ matrix.system }} steps: - name: Checkout - uses: actions/checkout@v2 - - - name: Install Tabby OSS Cad Suite (from YosysHQ) - uses: YosysHQ/setup-oss-cad-suite@v1 - with: - osscadsuite-version: '2021-11-09' - + uses: actions/checkout@v3 + - name: Install Tabby OSS Cad Suite + uses: ./.github/workflows/setup-oss-cad-suite - name: Install Espresso - run: | - cd /tmp - wget https://github.com/chipsalliance/espresso/releases/download/v${{ matrix.espresso }}/x86_64-linux-gnu-espresso - chmod +x x86_64-linux-gnu-espresso - sudo mv x86_64-linux-gnu-espresso /usr/local/bin/espresso - espresso || true - + uses: ./.github/workflows/install-espresso + with: + version: ${{ matrix.espresso }} - name: Setup Scala - uses: olafurpg/setup-scala@v10 + uses: actions/setup-java@v3 with: + distribution: 'adopt' java-version: ${{ matrix.jvm }} - - name: Cache Scala - uses: coursier/cache-action@v5 + cache: 'sbt' - name: Use Treadle for Pull Requests if: github.event_name == 'pull_request' run: echo "CHISEL3_CI_USE_TREADLE=1" >> $GITHUB_ENV @@ -57,13 +49,13 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Scala - uses: olafurpg/setup-scala@v10 + uses: actions/setup-java@v3 with: - java-version: "adopt@1.11" - - name: Cache Scala - uses: coursier/cache-action@v5 + distribution: 'adopt' + java-version: '11' + cache: 'sbt' - name: Check Formatting run: sbt scalafmtCheckAll - name: Documentation @@ -74,24 +66,17 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 - - name: Install Tabby OSS Cad Suite (from YosysHQ) - uses: YosysHQ/setup-oss-cad-suite@v1 - with: - osscadsuite-version: '2021-11-09' + uses: actions/checkout@v3 + - name: Install Tabby OSS Cad Suite + uses: ./.github/workflows/setup-oss-cad-suite - name: Install Espresso - run: | - cd /tmp - wget https://github.com/chipsalliance/espresso/releases/download/v2.4/x86_64-linux-gnu-espresso - chmod +x x86_64-linux-gnu-espresso - sudo mv x86_64-linux-gnu-espresso /usr/local/bin/espresso - espresso || true + uses: ./.github/workflows/install-espresso - name: Setup Scala - uses: olafurpg/setup-scala@v10 + uses: actions/setup-java@v3 with: - java-version: "adopt@1.11" - - name: Cache Scala - uses: coursier/cache-action@v5 + distribution: 'adopt' + java-version: '11' + cache: 'sbt' - name: Integration Tests run: sbt integrationTests/test @@ -108,7 +93,7 @@ jobs: success: ${{ steps.setoutput.outputs.success }} steps: - id: setoutput - run: echo "::set-output name=success::true" + run: echo "success=true" >> $GITHUB_OUTPUT # Related to check-tests above, this job _always_ runs (even if tests fail # and thus check-steps is skipped). This two sentinel job approach avoids an @@ -126,10 +111,10 @@ jobs: - run: | PASSED="${{ needs.check-tests.outputs.success }}" if [[ $PASSED == "true" ]]; then - echo "All tests passed!" + echo "### All tests passed! :rocket:" >> $GITHUB_STEP_SUMMARY exit 0 else - echo "One or more tests FAILED!" + echo "### One or more tests FAILED! :bangbang:" >> $GITHUB_STEP_SUMMARY exit 1 fi @@ -142,13 +127,13 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Scala - uses: olafurpg/setup-scala@v10 + uses: actions/setup-java@v3 with: - java-version: adopt@1.8 - - name: Cache Scala - uses: coursier/cache-action@v5 + distribution: 'adopt' + java-version: '8' + cache: 'sbt' - name: Setup GPG (for Publish) uses: olafurpg/setup-gpg@v3 - name: Publish @@ -38,6 +38,7 @@ lazy val commonSettings = Seq ( ) lazy val publishSettings = Seq ( + versionScheme := Some("pvp"), publishMavenStyle := true, publishArtifact in Test := false, pomIncludeRepository := { x => false }, diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 50093333..dddc0d5d 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -256,8 +256,13 @@ package experimental { def fullModulePorts(target: BaseModule): Seq[(String, Data)] = { def getPortNames(name: String, data: Data): Seq[(String, Data)] = Seq(name -> data) ++ (data match { case _: Element => Seq() - case r: Record => r.elements.toSeq.flatMap { case (eltName, elt) => getPortNames(s"${name}_${eltName}", elt) } - case v: Vec[_] => v.zipWithIndex.flatMap { case (elt, index) => getPortNames(s"${name}_${index}", elt) } + case r: Record => + r.elements.toSeq.flatMap { + case (eltName, elt) => + if (r._isOpaqueType) { getPortNames(s"${name}", elt) } + else { getPortNames(s"${name}_${eltName}", elt) } + } + case v: Vec[_] => v.zipWithIndex.flatMap { case (elt, index) => getPortNames(s"${name}_${index}", elt) } }) modulePorts(target).flatMap { case (name, data) => diff --git a/core/src/main/scala/chisel3/IO.scala b/core/src/main/scala/chisel3/IO.scala new file mode 100644 index 00000000..1a28db1e --- /dev/null +++ b/core/src/main/scala/chisel3/IO.scala @@ -0,0 +1,41 @@ +package chisel3 + +import chisel3.internal.requireIsChiselType // Fix ambiguous import +import chisel3.internal.Builder +import chisel3.internal.sourceinfo.SourceInfo + +object IO { + + /** Constructs a port for the current Module + * + * This must wrap the datatype used to set the io field of any Module. + * i.e. All concrete modules must have defined io in this form: + * [lazy] val io[: io type] = IO(...[: io type]) + * + * Items in [] are optional. + * + * The granted iodef must be a chisel type and not be bound to hardware. + * + * Also registers a Data as a port, also performing bindings. Cannot be called once ports are + * requested (so that all calls to ports will return the same information). + * Internal API. + */ + def apply[T <: Data](iodef: T): T = { + val module = Module.currentModule.get // Impossible to fail + require(!module.isClosed, "Can't add more ports after module close") + requireIsChiselType(iodef, "io type") + + // Clone the IO so we preserve immutability of data types + val iodefClone = + try { + iodef.cloneTypeFull + } catch { + // For now this is going to be just a deprecation so we don't suddenly break everyone's code + case e: AutoClonetypeException => + Builder.deprecated(e.getMessage, Some(s"${iodef.getClass}")) + iodef + } + module.bindIoInPlace(iodefClone) + iodefClone + } +} diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index e5c2a848..a2d5cec6 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -200,42 +200,10 @@ abstract class Module(implicit moduleCompileOptions: CompileOptions) extends Raw } package experimental { - - import chisel3.internal.requireIsChiselType // Fix ambiguous import - object IO { - - /** Constructs a port for the current Module - * - * This must wrap the datatype used to set the io field of any Module. - * i.e. All concrete modules must have defined io in this form: - * [lazy] val io[: io type] = IO(...[: io type]) - * - * Items in [] are optional. - * - * The granted iodef must be a chisel type and not be bound to hardware. - * - * Also registers a Data as a port, also performing bindings. Cannot be called once ports are - * requested (so that all calls to ports will return the same information). - * Internal API. - */ + @deprecated("chisel3.experimental.IO is deprecated, use chisel3.IO instead", "Chisel 3.5") def apply[T <: Data](iodef: T): T = { - val module = Module.currentModule.get // Impossible to fail - require(!module.isClosed, "Can't add more ports after module close") - requireIsChiselType(iodef, "io type") - - // Clone the IO so we preserve immutability of data types - val iodefClone = - try { - iodef.cloneTypeFull - } catch { - // For now this is going to be just a deprecation so we don't suddenly break everyone's code - case e: AutoClonetypeException => - Builder.deprecated(e.getMessage, Some(s"${iodef.getClass}")) - iodef - } - module.bindIoInPlace(iodefClone) - iodefClone + chisel3.IO.apply(iodef) } } } @@ -753,7 +721,7 @@ package experimental { * TODO(twigg): Specifically walk the Data definition to call out which nodes * are problematic. */ - protected def IO[T <: Data](iodef: T): T = chisel3.experimental.IO.apply(iodef) + protected def IO[T <: Data](iodef: T): T = chisel3.IO.apply(iodef) // // Internal Functions diff --git a/core/src/main/scala/chisel3/SeqUtils.scala b/core/src/main/scala/chisel3/SeqUtils.scala index b1136120..9d975349 100644 --- a/core/src/main/scala/chisel3/SeqUtils.scala +++ b/core/src/main/scala/chisel3/SeqUtils.scala @@ -64,7 +64,10 @@ private[chisel3] object SeqUtils { if (in.size == 1) { in.head._2 } else { - Mux(in.head._1, in.head._2, priorityMux(in.tail)) + val r = in.view.reverse + r.tail.foldLeft(r.head._2) { + case (alt, (sel, elt)) => Mux(sel, elt, alt) + } } } diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index 39131943..42ec9666 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -72,7 +72,7 @@ package object experimental { val ports: Seq[Data] = gen.elements.toSeq.reverse.map { case (name, data) => - val p = IO(coerceDirection(chiselTypeClone(data).asInstanceOf[Data])) + val p = chisel3.IO(coerceDirection(chiselTypeClone(data).asInstanceOf[Data])) p.suggestName(name) p diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index e3dfff09..d06b7992 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -17,18 +17,29 @@ import chisel3.internal.Builder.Prefix import logger.LazyLogging import scala.collection.mutable +import scala.annotation.tailrec private[chisel3] class Namespace(keywords: Set[String]) { + // This HashMap is compressed, not every name in the namespace is present here. + // If the same name is requested multiple times, it only takes 1 entry in the HashMap and the + // value is incremented for each time the name is requested. + // Names can be requested that collide with compressed sets of names, thus the algorithm for + // checking if a name is present in the Namespace is more complex than just checking the HashMap, + // see getIndex below. private val names = collection.mutable.HashMap[String, Long]() def copyTo(other: Namespace): Unit = names.foreach { case (s: String, l: Long) => other.names(s) = l } for (keyword <- keywords) names(keyword) = 1 - private def rename(n: String): String = { - val index = names(n) + @tailrec + private def rename(n: String, index: Long): String = { val tryName = s"${n}_${index}" - names(n) = index + 1 - if (this contains tryName) rename(n) else tryName + if (names.contains(tryName)) { + rename(n, index + 1) + } else { + names(n) = index + 1 + tryName + } } private def sanitize(s: String, leadingDigitOk: Boolean = false): String = { @@ -40,14 +51,50 @@ private[chisel3] class Namespace(keywords: Set[String]) { if (headOk) res else s"_$res" } - def contains(elem: String): Boolean = names.contains(elem) + /** Checks if `n` ends in `_\d+` and returns the substring before `_` if so, null otherwise */ + // TODO can and should this be folded in to sanitize? Same iteration as the forall? + private def prefix(n: String): Int = { + // This is micro-optimized because it runs on every single name + var i = n.size - 1 + while (i > 0 && n(i).isDigit) { + i -= 1 + } + // Will get i == 0 for all digits or _\d+ with empty prefix, those have no prefix so returning 0 is correct + if (i == n.size) 0 // no digits + else if (n(i) != '_') 0 // no _ + else i + } + + // Gets the current index for this name, None means it is not contained in the Namespace + private def getIndex(elem: String): Option[Long] = + names.get(elem).orElse { + // This exact name isn't contained, but if we end in _<idx>, we need to check our prefix + val maybePrefix = prefix(elem) + if (maybePrefix == 0) None + else { + // If we get a prefix collision and our index is taken, we start disambiguating with _<idx>_1 + names + .get(elem.take(maybePrefix)) + .filter { prefixIdx => + val ourIdx = elem.drop(maybePrefix + 1).toInt + // The namespace starts disambiguating at _1 so _0 is a false collision case + ourIdx != 0 && prefixIdx > ourIdx + } + .map(_ => 1) + } + } + + def contains(elem: String): Boolean = getIndex(elem).isDefined // leadingDigitOk is for use in fields of Records def name(elem: String, leadingDigitOk: Boolean = false): String = { val sanitized = sanitize(elem, leadingDigitOk) - val result = if (this.contains(sanitized)) rename(sanitized) else sanitized - names(result) = 1 - result + getIndex(sanitized) match { + case Some(idx) => rename(sanitized, idx) + case None => + names(sanitized) = 1 + sanitized + } } } diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala index 067dd6f8..ce2da3c0 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -107,8 +107,10 @@ object decoder extends LazyLogging { { bitSets.zipWithIndex.flatMap { case (bs, i) => - bs.terms.map(bp => s"${bp.rawString}->${if (errorBit) "0"}${"0" * (bitSets.size - i - 1)}1${"0" * i}") - } ++ Seq(s"${if (errorBit) "1"}${"?" * bitSets.size}") + bs.terms.map(bp => + s"${bp.rawString}->${if (errorBit) "0" else ""}${"0" * (bitSets.size - i - 1)}1${"0" * i}" + ) + } ++ Seq(s"${if (errorBit) "1" ++ "0" * bitSets.size else "?" * bitSets.size}") }.mkString("\n") ) ) diff --git a/src/test/scala/chisel3/internal/NamespaceSpec.scala b/src/test/scala/chisel3/internal/NamespaceSpec.scala new file mode 100644 index 00000000..fd808ff0 --- /dev/null +++ b/src/test/scala/chisel3/internal/NamespaceSpec.scala @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.internal + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers._ + +class NamespaceSpec extends AnyFlatSpec { + behavior.of("Namespace") + + they should "support basic disambiguation" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x") should be("x") + name("x") should be("x_1") + name("x") should be("x_2") + } + + they should "support explicit <prefix>_# names before <prefix> names" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x_1") should be("x_1") + name("x_2") should be("x_2") + name("x") should be("x") + name("x") should be("x_3") + } + + they should "support explicit <prefix>_# names in the middle of <prefix> names" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x") should be("x") + name("x") should be("x_1") + name("x_1") should be("x_1_1") + name("x_2") should be("x_2") + name("x") should be("x_3") + } + + // For some reason, multi-character names tickled a different failure mode than single character + they should "support explicit <prefix>_# names in the middle of longer <prefix> names" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("foo") should be("foo") + name("foo") should be("foo_1") + name("foo_1") should be("foo_1_1") + name("foo_2") should be("foo_2") + name("foo") should be("foo_3") + } + + they should "support collisions in recursively growing names" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x") should be("x") + name("x") should be("x_1") + name("x_1") should be("x_1_1") + name("x_1") should be("x_1_2") + name("x_1_1") should be("x_1_1_1") + name("x_1_1") should be("x_1_1_2") + } + + they should "support collisions in recursively shrinking names" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x_1_1") should be("x_1_1") + name("x_1_1") should be("x_1_1_1") + name("x_1") should be("x_1") + name("x_1") should be("x_1_2") + name("x") should be("x") + name("x") should be("x_2") + } + + // The namespace never generates names with _0 so it's actually a false collision case + they should "properly handle false collisions with signals ending in _0" in { + val namespace = Namespace.empty + val name = namespace.name(_, false) + name("x") should be("x") + name("x") should be("x_1") + name("x_0") should be("x_0") + name("x") should be("x_2") + name("x_0") should be("x_0_1") + } +} diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 3414ec8a..5a5bcf67 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -284,6 +284,26 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { testStrings.foreach(x => assert(x == "~NestedRecordModule|InnerModule>io.foo")) } + they should "work correctly with DataMirror in nested OpaqueType Records" in { + var mod: NestedRecordModule = null + ChiselStage.elaborate { mod = new NestedRecordModule; mod } + val ports = chisel3.experimental.DataMirror.fullModulePorts(mod.inst) + val expectedPorts = Seq( + ("clock", mod.inst.clock), + ("reset", mod.inst.reset), + ("io", mod.inst.io), + ("io_bar", mod.inst.io.bar), + ("io_bar", mod.inst.io.bar.k), + ("io_bar", mod.inst.io.bar.k.k), + ("io_bar", mod.inst.io.bar.k.k.elements.head._2), + ("io_foo", mod.inst.io.foo), + ("io_foo", mod.inst.io.foo.k), + ("io_foo", mod.inst.io.foo.k.k), + ("io_foo", mod.inst.io.foo.k.k.elements.head._2) + ) + ports shouldBe expectedPorts + } + they should "work correctly when connecting nested OpaqueType elements" in { val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } nestedRecordChirrtl should include("input in : UInt<8>") diff --git a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala index ebb7cbdb..fb3f64c7 100644 --- a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala +++ b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala @@ -55,9 +55,11 @@ class FlatIOSpec extends ChiselFlatSpec { val bar = Analog(8.W) } class MyModule extends RawModule { - val in = IO(Flipped(new MyBundle)) - val out = IO(new MyBundle) - out <> in + val io = FlatIO(new Bundle { + val in = Flipped(new MyBundle) + val out = new MyBundle + }) + io.out <> io.in } val chirrtl = emitChirrtl(new MyModule) chirrtl should include("out.foo <= in.foo") diff --git a/src/test/scala/chiselTests/util/BitSetSpec.scala b/src/test/scala/chiselTests/util/BitSetSpec.scala index dd66ba40..cf5f54cf 100644 --- a/src/test/scala/chiselTests/util/BitSetSpec.scala +++ b/src/test/scala/chiselTests/util/BitSetSpec.scala @@ -110,9 +110,36 @@ class BitSetSpec extends AnyFlatSpec with Matchers { "b11??????" ) ), - true + errorBit = true ) }) } + it should "be decoded with DontCare error" in { + import chisel3._ + import chisel3.util.experimental.decode.decoder + // [0 - 256] part into: [0 - 31], [32 - 47, 64 - 127], [192 - 255] + // "0011????" "10??????" is empty to error + chisel3.stage.ChiselStage.emitSystemVerilog(new Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(4.W))) + out := decoder.bitset( + in, + Seq( + BitSet.fromString( + "b000?????" + ), + BitSet.fromString( + """b0010???? + |b01?????? + |""".stripMargin + ), + BitSet.fromString( + "b11??????" + ) + ), + errorBit = false + ) + }) + } } diff --git a/src/test/scala/chiselTests/util/PriorityMuxSpec.scala b/src/test/scala/chiselTests/util/PriorityMuxSpec.scala new file mode 100644 index 00000000..32cf2431 --- /dev/null +++ b/src/test/scala/chiselTests/util/PriorityMuxSpec.scala @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests.util + +import chisel3._ +import chisel3.util.{is, switch, Counter, PriorityMux} +import chisel3.testers.BasicTester +import chisel3.stage.ChiselStage.emitChirrtl + +import chiselTests.ChiselFlatSpec + +class PriorityMuxTester extends BasicTester { + + val sel = Wire(UInt(3.W)) + sel := 0.U // default + + val elts = Seq(5.U, 6.U, 7.U) + val muxed = PriorityMux(sel, elts) + + // Priority is given to lowest order bit + val tests = Seq( + 1.U -> elts(0), + 2.U -> elts(1), + 3.U -> elts(0), + 4.U -> elts(2), + 5.U -> elts(0), + 6.U -> elts(1), + 7.U -> elts(0) + ) + val (cycle, done) = Counter(0 until tests.size + 1) + + for (((in, out), idx) <- tests.zipWithIndex) { + when(cycle === idx.U) { + sel := in + assert(muxed === out) + } + } + + when(done) { + stop() + } +} + +class PriorityMuxSpec extends ChiselFlatSpec { + behavior.of("PriorityMux") + + it should "be functionally correct" in { + assertTesterPasses(new PriorityMuxTester) + } + + it should "be stack safe" in { + emitChirrtl(new RawModule { + val n = 1 << 15 + val in = IO(Input(Vec(n, UInt(8.W)))) + val sel = IO(Input(UInt(n.W))) + val out = IO(Output(UInt(8.W))) + out := PriorityMux(sel, in) + }) + } +} |
