├── .github └── workflows │ └── test.yml ├── .gitignore ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt └── src ├── main └── scala │ └── fixedpoint │ ├── BinaryPoint.scala │ ├── FixedPoint.scala │ ├── ForceElementwiseConnect.scala │ ├── package.scala │ └── shadow │ ├── MuxDefs.scala │ └── Util.scala └── test └── scala ├── AsTypeOfTester.scala ├── BundleLiteralSpec.scala ├── ChiselSpec.scala ├── ConnectSpec.scala ├── DataEqualitySpec.scala ├── DataPrint.scala ├── FixedPointSpec.scala ├── LiteralExtractorSpec.scala ├── OneHotMuxSpec.scala └── VecLiteralSpec.scala /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: sbt test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - 'chisel*.*' 8 | 9 | pull_request: 10 | branches: 11 | - master 12 | - 'chisel*.*' 13 | 14 | permissions: 15 | contents: read 16 | 17 | env: 18 | JDK_VERSION: 20 19 | JDK_DIST: temurin 20 | VERILATOR_REPO: https://github.com/verilator/verilator 21 | VERILATOR_DIR: verilator 22 | VERILATOR_VERSION_TAG: v4.228 23 | FIRTOOL_VERSION: v1.62.0 24 | FIRTOOL_VERSION_TAG: firtool-1.62.0 25 | 26 | jobs: 27 | test: 28 | 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - name: Check out repo 33 | uses: actions/checkout@v3 34 | - name: Set up JDK ${{ env.JDK_VERSION }} (${{ env.JDK_DIST }}) 35 | uses: actions/setup-java@v3 36 | with: 37 | java-version: ${{ env.JDK_VERSION }} 38 | distribution: ${{ env.JDK_DIST }} 39 | cache: 'sbt' 40 | - name: Install firtool ${{ env.FIRTOOL_VERSION }} 41 | run: | 42 | wget -q -O - "https://github.com/llvm/circt/releases/download/${{ env.FIRTOOL_VERSION_TAG }}/firrtl-bin-linux-x64.tar.gz" | tar -zx 43 | sudo mv "${{ env.FIRTOOL_VERSION_TAG }}/bin/firtool" /usr/local/bin 44 | - name: "Cache: Verilator ${{ env.VERILATOR_VERSION_TAG }}" 45 | uses: actions/cache@v3 46 | id: install-cache 47 | with: 48 | path: ${{ env.VERILATOR_DIR }} 49 | key: ${{ runner.os }}-${{ runner.arch }}-verilator-${{ env.VERILATOR_VERSION_TAG }} 50 | 51 | - name: Install Verilator ${{ env.VERILATOR_VERSION_TAG }} from cache 52 | if: steps.install-cache.outputs.cache-hit == 'true' 53 | run: | 54 | sudo apt-get update -y 55 | sudo apt-get install -y git help2man perl python3 make autoconf g++ flex bison ccache 56 | sudo apt-get install -y numactl perl-doc 57 | sudo apt-get install -my libgoogle-perftools-dev || true 58 | sudo apt-get install -my libfl2 || true # Ubuntu only (ignore if gives error) 59 | sudo apt-get install -my libfl-dev || true # Ubuntu only (ignore if gives error) 60 | sudo apt-get install -my zlibc zlib1g zlib1g-dev || true # Ubuntu only (ignore if gives error) 61 | 62 | cd "$VERILATOR_DIR" 63 | sudo make install 64 | verilator --version 65 | - name: Install Verilator ${{ env.VERILATOR_VERSION_TAG }} from scratch 66 | if: steps.install-cache.outputs.cache-hit != 'true' 67 | run: | 68 | sudo apt-get update -y 69 | sudo apt-get install -y git help2man perl python3 make autoconf g++ flex bison ccache 70 | sudo apt-get install -y numactl perl-doc 71 | sudo apt-get install -my libgoogle-perftools-dev || true 72 | sudo apt-get install -my libfl2 || true # Ubuntu only (ignore if gives error) 73 | sudo apt-get install -my libfl-dev || true # Ubuntu only (ignore if gives error) 74 | sudo apt-get install -my zlibc zlib1g zlib1g-dev || true # Ubuntu only (ignore if gives error) 75 | 76 | git clone "$VERILATOR_REPO" "$VERILATOR_DIR" 77 | cd "$VERILATOR_DIR" 78 | git checkout "$VERILATOR_VERSION_TAG" 79 | autoconf 80 | ./configure 81 | make -j "$(nproc)" 82 | sudo make install 83 | verilator --version 84 | 85 | - name: Check scalafmt 86 | run: sbt scalafmtCheckAll 87 | - name: Run tests 88 | run: sbt test 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | generated/ 3 | /lib/firrtl.jar 4 | .classpath 5 | .idea 6 | .idea_modules/ 7 | .project 8 | target/ 9 | project/project 10 | project/target 11 | *.iml 12 | *.swp 13 | *.fir 14 | *.v 15 | *.json 16 | test_run_dir 17 | *~ 18 | \#*\# 19 | .\#* 20 | .metals 21 | .bloop 22 | .scala-build 23 | metals.sbt 24 | version.txt 25 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 2.6.4 2 | 3 | maxColumn = 120 4 | align = most 5 | continuationIndent.defnSite = 2 6 | assumeStandardLibraryStripMargin = true 7 | docstrings = ScalaDoc 8 | lineEndings = preserve 9 | includeCurlyBraceInSelectChains = false 10 | danglingParentheses = true 11 | 12 | align.tokens.add = [ 13 | { 14 | code = ":" 15 | } 16 | ] 17 | 18 | newlines.alwaysBeforeCurlyBraceLambdaParams = false 19 | newlines.alwaysBeforeMultilineDef = false 20 | newlines.implicitParamListModifierForce = [before] 21 | 22 | verticalMultiline.atDefnSite = true 23 | 24 | optIn.annotationNewlines = true 25 | 26 | rewrite.rules = [SortImports, PreferCurlyFors, AvoidInfix] 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | ------------------------------------------------------------------------ 180 | Note: 181 | Individual files contain the following tag instead of the full license text. 182 | 183 | // SPDX-License-Identifier: Apache-2.0 184 | 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fixed-Point User Library for Chisel [![Test](https://github.com/ucb-bar/fixedpoint/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/ucb-bar/fixedpoint/actions) 2 | 3 | ## Introduction 4 | 5 | This is an attempt to add the FixedPoint type to Chisel as a user-level library. The main motivation behind this is the deprecation of FixedPoint starting from [Chisel v3.6](https://github.com/chipsalliance/chisel/releases/v3.6.0), where Chisel is transitioning from Scala for Chisel compiler (SFC) to MLIR FIRRTL compiler. In this transition, FixedPoint [was dropped at the FIRRTL level](https://github.com/chipsalliance/chisel/issues/3161), meaning that FixedPoint would need to be re-implemented as something that depends on the remaining existing Chisel types, and by using Chisel's public API. This library aims to provide that user-level implementation. 6 | 7 | The main goal of this library is to faithfully reproduce Chisel's established FixedPoint interface, so that existing code that depends on it can just replace Chisel's FixedPoint imports with this library's FixedPoint imports and have the code 8 | work just as it did before. To that end, the following classes/traits have been (re)implemented: 9 | * FixedPoint 10 | * BinaryPoint, KnownBinaryPoint, UnknownBinaryPoint 11 | * HasBinaryPoint 12 | 13 | Currently, this library works with [Chisel v6.5.0](https://github.com/chipsalliance/chisel/releases/v6.5.0). 14 | 15 | ## Usage 16 | 17 | Here is an example module using this library's FixedPoint type: 18 | 19 | ```scala 20 | import chisel3._ 21 | import circt.stage.ChiselStage 22 | import fixedpoint._ 23 | 24 | class Example extends Module { 25 | val in = IO(Input(FixedPoint(8.W, 4.BP))) 26 | val out1 = IO(Output(FixedPoint(8.W, 5.BP))) 27 | val out2 = IO(Output(FixedPoint())) 28 | 29 | out1 := in 30 | out2 := WireDefault(3.14.F(8.BP)) 31 | } 32 | 33 | object ExampleApp extends App { 34 | println(ChiselStage.emitSystemVerilog(new Example)) 35 | } 36 | ``` 37 | 38 | This outputs the following SystemVerilog code: 39 | 40 | ```systemverilog 41 | // Generated by CIRCT firtool-1.43.0 42 | module Example( // :3:10 43 | input clock, // :4:11 44 | reset, // :5:11 45 | input [7:0] in, // src/main/scala/Example.scala:6:14 46 | output [7:0] out1, // src/main/scala/Example.scala:7:16 47 | output [10:0] out2 // src/main/scala/Example.scala:8:16 48 | ); 49 | 50 | assign out1 = {in[6:0], 1'h0}; // :3:10, src/main/scala/Example.scala:10:8 51 | assign out2 = 11'h324; // :3:10, src/main/scala/Example.scala:11:22 52 | endmodule 53 | ``` 54 | 55 | ## How It Works 56 | 57 | FixedPoint is implemented as an extension of `Record`, which has one *anonymous* data field of type `SInt`; it is also an [opaque type](https://github.com/chipsalliance/chisel/blob/v5.1.0/core/src/main/scala/chisel3/experimental/OpaqueType.scala). Most of the arithmetic involving FixedPoints has been delegated to the `SInt` arithmetic of the underlying data field, where shift operations are first used to align the data of FixedPoints that have different binary points. Connect methods have also been overridden to account for data alignment of FixedPoints with different binary points, and to implement binary point inference. 58 | 59 | ## Limitations 60 | 61 | It was challenging to implement FixedPoint using Chisel's public API as some of the needed functionality for FixedPoints was originally implemented in Chisel's package-private objects, which cannot be accessed or altered from a user-level library. Due to this issue, some of the original FixedPoint functionality could not be implemented without limited workarounds. Here is the current list of limitations of this implementation of FixedPoint: 62 | * FixedPoints with different binary points are not aligned properly when used inside Chisel's Muxes (`Mux`, `Mux1H`, `PriorityMux`, `MuxLookup`, `MuxCase`). To that end, these objects have been redefined in the package `fixedpoint.shadow` to align FixedPoints by width and binary point before calling Chisel's corresponding Mux objects. In order to make FixedPoint work properly with Muxes, you have to import the new Mux definitions as follows: 63 | ```scala 64 | import fixedpoint.shadow.{Mux, Mux1H, PriorityMux, MuxLookup, MuxCase} 65 | ``` 66 | * Records with inferred widths cannot be used inside `Mux1H`. If you want to use FixedPoints inside a `Mux1H`, make sure that both width and binary point are specified in advance. 67 | * FixedPoints do not connect properly if they are nested inside a `Bundle` or `Record`. If you have a bundle/record that has a FixedPoint field, you will have to extend it with the `ForceElementwiseConnect` trait. If you have a bundle `Foo` defined as: 68 | ```scala 69 | class Foo extends Bundle { 70 | val data = FixedPoint(8.W, 4.BP) 71 | } 72 | ``` 73 | ...then you will have to redefine it as: 74 | ```scala 75 | class Foo(implicit val ct: ClassTag[Foo]) extends Bundle 76 | with ForceElementwiseConnect[Foo] { 77 | val data = FixedPoint(8.W, 4.BP) 78 | } 79 | ``` 80 | If you have multiple levels of nesting inside Bundles, each bundle at every level 81 | needs to extend `ForceElementwiseConnect`. 82 | * FixedPoints do not connect properly if they are part of a `Vec`. **Currently, there is no solution available for this problem.** 83 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | scalaVersion := "2.13.14" 2 | scalacOptions += "-language:higherKinds" 3 | addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.13.3" cross CrossVersion.full) 4 | 5 | scalacOptions += "-Ydelambdafy:inline" 6 | scalacOptions ++= Seq( 7 | "-deprecation", 8 | "-encoding", "UTF-8", 9 | "-feature", 10 | "-unchecked", 11 | "-Xfatal-warnings", 12 | "-language:reflectiveCalls", 13 | "-Ymacro-annotations" 14 | ) 15 | val chiselVersion = "6.5.0" 16 | addCompilerPlugin("org.chipsalliance" %% "chisel-plugin" % chiselVersion cross CrossVersion.full) 17 | libraryDependencies ++= Seq( 18 | "org.chipsalliance" %% "chisel" % chiselVersion, 19 | "org.scalatest" %% "scalatest" % "3.2.15" % "test", 20 | "org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0" % "test", 21 | ) 22 | 23 | Test / parallelExecution := false 24 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.8.2 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | val scalafmtPluginVersion = "2.4.6" 2 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % scalafmtPluginVersion) 3 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/BinaryPoint.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // This file contains definitions of the following traits/classes/objects taken from Chisel's deprecated code: 4 | // - BinaryPoint 5 | // - KnownBinaryPoint 6 | // - UnknownBinaryPoint 7 | // - HasBinaryPoint 8 | // 9 | // HasBinaryPoint uses Record as a self-type as opposed to Bits used in Chisel's code. 10 | 11 | package fixedpoint 12 | 13 | import chisel3.{ChiselException, Num, Record} 14 | 15 | /** Chisel types that have binary points support retrieving 16 | * literal values as `Double` or `BigDecimal` 17 | */ 18 | 19 | object NumBP { 20 | def toDouble(value: BigInt, binaryPoint: BinaryPoint): Double = { 21 | binaryPoint match { 22 | case KnownBinaryPoint(n) => Num.toDouble(value, n) 23 | case x => 24 | throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") 25 | } 26 | } 27 | 28 | def toBigDecimal(value: BigInt, binaryPoint: BinaryPoint): BigDecimal = { 29 | binaryPoint match { 30 | case KnownBinaryPoint(n) => Num.toBigDecimal(value, n) 31 | case x => 32 | throw new ChiselException(s"Error converting BigDecimal $value to BigInt, binary point must be known, not $x") 33 | } 34 | } 35 | } 36 | 37 | trait HasBinaryPoint { 38 | self: Record => 39 | 40 | def binaryPoint: BinaryPoint 41 | 42 | /** Return the [[Double]] value of this instance if it is a Literal 43 | * 44 | * @note this method may throw an exception if the literal value won't fit in a Double 45 | */ 46 | def litToDoubleOption: Option[Double] = { 47 | litOption match { 48 | case Some(bigInt: BigInt) => 49 | Some(NumBP.toDouble(bigInt, binaryPoint)) 50 | case _ => None 51 | } 52 | } 53 | 54 | /** Return the double value of this instance assuming it is a literal (convenience method) 55 | */ 56 | def litToDouble: Double = litToDoubleOption.get 57 | 58 | /** Return the [[BigDecimal]] value of this instance if it is a Literal 59 | * 60 | * @note this method may throw an exception if the literal value won't fit in a BigDecimal 61 | */ 62 | def litToBigDecimalOption: Option[BigDecimal] = { 63 | litOption match { 64 | case Some(bigInt: BigInt) => 65 | Some(NumBP.toBigDecimal(bigInt, binaryPoint)) 66 | case _ => None 67 | } 68 | } 69 | 70 | /** Return the [[BigDecimal]] value of this instance assuming it is a literal (convenience method) 71 | * 72 | * @return 73 | */ 74 | def litToBigDecimal: BigDecimal = litToBigDecimalOption.get 75 | } 76 | 77 | object BinaryPoint { 78 | def apply(x: Int): BinaryPoint = KnownBinaryPoint(x) 79 | def apply(): BinaryPoint = UnknownBinaryPoint 80 | } 81 | 82 | sealed abstract class BinaryPoint { 83 | type W = Int 84 | def max(that: BinaryPoint): BinaryPoint = this.op(that, _ max _) 85 | def +(that: BinaryPoint): BinaryPoint = this.op(that, _ + _) 86 | def +(that: Int): BinaryPoint = this.op(this, (a, b) => a + that) 87 | def shiftRight(that: Int): BinaryPoint = this.op(this, (a, b) => 0.max(a - that)) 88 | def dynamicShiftLeft(that: BinaryPoint): BinaryPoint = 89 | this.op(that, (a, b) => a + (1 << b) - 1) 90 | 91 | def known: Boolean 92 | def get: W 93 | protected def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint 94 | } 95 | 96 | case object UnknownBinaryPoint extends BinaryPoint { 97 | def known: Boolean = false 98 | def get: Int = None.get 99 | def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = this 100 | override def toString: String = "" 101 | } 102 | 103 | sealed case class KnownBinaryPoint(value: Int) extends BinaryPoint { 104 | def known: Boolean = true 105 | def get: Int = value 106 | def op(that: BinaryPoint, f: (W, W) => W): BinaryPoint = that match { 107 | case KnownBinaryPoint(x) => KnownBinaryPoint(f(value, x)) 108 | case _ => that 109 | } 110 | override def toString: String = s"<<${value.toString}>>" 111 | } 112 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/FixedPoint.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // This file contains the definitions of FixedPoint class and companion object. Much of Chisel's original code 4 | // is reused, but Record is inherited from instead of Bits. Relevant methods from Bits and Chisel's FixedPoint 5 | // have also been implemented in order to maximally replicate the original FixedPoint interface. 6 | 7 | // Notes: 8 | // - Not being able to extend cloneSuperType behavior makes it difficult to use user-defined FixedPoint with Muxes, 9 | // and also to implement typeEquivalent fully 10 | // - Not being able to extend MonoConnect behavior makes it difficult to properly connect FixedPoints with 11 | // different BinaryPoints, especially if inside other Bundles and Vecs 12 | // - Cannot Mux1H with aggregates with inferred widths 13 | 14 | package fixedpoint 15 | 16 | import chisel3._ 17 | import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor 18 | import chisel3.experimental.OpaqueType 19 | import chisel3.experimental.SourceInfo 20 | import chisel3.internal.sourceinfo.{SourceInfoTransform, SourceInfoWhiteboxTransform} 21 | 22 | import scala.collection.immutable.SeqMap 23 | import scala.language.experimental.macros 24 | import chisel3.util.Cat 25 | 26 | object FixedPoint extends NumObject { 27 | 28 | /** Create a FixedPoint type with inferred width. */ 29 | def apply(): FixedPoint = apply(UnknownWidth(), BinaryPoint()) 30 | 31 | /** Create a FixedPoint type or port with fixed width. */ 32 | def apply(width: Width, binaryPoint: BinaryPoint): FixedPoint = new FixedPoint(width, binaryPoint) 33 | 34 | /** Create a FixedPoint literal with inferred width from BigInt. 35 | * Use PrivateObject to force users to specify width and binaryPoint by name 36 | */ 37 | def fromBigInt(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { 38 | apply(value, width, binaryPoint) 39 | } 40 | 41 | /** Create a FixedPoint literal with inferred width from BigInt. 42 | * Use PrivateObject to force users to specify width and binaryPoint by name 43 | */ 44 | def fromBigInt(value: BigInt, binaryPoint: BinaryPoint = 0.BP): FixedPoint = { 45 | apply(value, UnknownWidth(), binaryPoint) 46 | } 47 | 48 | /** Create a FixedPoint literal with inferred width from BigInt. 49 | * Use PrivateObject to force users to specify width and binaryPoint by name 50 | */ 51 | def fromBigInt(value: BigInt, width: Int, binaryPoint: Int): FixedPoint = 52 | if (width == -1) { 53 | apply(value, UnknownWidth(), BinaryPoint(binaryPoint)) 54 | } else { 55 | apply(value, KnownWidth(width), BinaryPoint(binaryPoint)) 56 | } 57 | 58 | /** Create a FixedPoint literal with inferred width from Double. 59 | * Use PrivateObject to force users to specify width and binaryPoint by name 60 | */ 61 | def fromDouble(value: Double, width: Width, binaryPoint: BinaryPoint): FixedPoint = { 62 | fromBigInt( 63 | toBigInt(value, binaryPoint.get), 64 | width = width, 65 | binaryPoint = binaryPoint 66 | ) 67 | } 68 | 69 | /** Create a FixedPoint literal with inferred width from BigDecimal. 70 | * Use PrivateObject to force users to specify width and binaryPoint by name 71 | */ 72 | def fromBigDecimal(value: BigDecimal, width: Width, binaryPoint: BinaryPoint): FixedPoint = { 73 | fromBigInt( 74 | toBigInt(value, binaryPoint.get), 75 | width = width, 76 | binaryPoint = binaryPoint 77 | ) 78 | } 79 | 80 | /** Create a FixedPoint port with specified width and binary position. */ 81 | def apply(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { 82 | val _width = if (width.known) width else (1 + value.bitLength).W 83 | new FixedPoint(_width, binaryPoint).Lit(_.data -> value.S(_width)) 84 | } 85 | 86 | /** Create a FixedPoint bundle with its data port connected to an SInt literal 87 | */ 88 | private[fixedpoint] def fromData( 89 | binaryPoint: BinaryPoint, 90 | data: Data, 91 | widthOption: Option[Width] = None 92 | )( 93 | implicit sourceInfo: SourceInfo 94 | ): FixedPoint = { 95 | val _new = Wire( 96 | FixedPoint( 97 | widthOption.getOrElse(recreateWidth(data)), 98 | binaryPoint 99 | ) 100 | ) 101 | _new.data := data.asTypeOf(_new.data) 102 | _new 103 | } 104 | 105 | private[fixedpoint] def recreateWidth[T <: Data](d: T): Width = { 106 | d.widthOption.fold[Width](UnknownWidth())(_.W) 107 | } 108 | 109 | /** Align all FixedPoints in a (possibly heterogeneous) sequence by width and binary point 110 | */ 111 | private[fixedpoint] def dataAligned[T <: Data](in: Iterable[T])(implicit sourceInfo: SourceInfo): Seq[T] = { 112 | val bps = in.collect { 113 | case el: FixedPoint => 114 | el.requireKnownBP() 115 | el.binaryPoint 116 | } 117 | 118 | val out = 119 | if (bps.isEmpty) in 120 | else { 121 | val maxBP = bps.fold(0.BP)(_.max(_)) 122 | val maxWidth = in.map { 123 | case el: FixedPoint => recreateWidth(el) + (maxBP.get - el.binaryPoint.get) 124 | case nonFp => recreateWidth(nonFp) 125 | }.fold(0.W)(_.max(_)) 126 | 127 | in.map { 128 | case el: FixedPoint => 129 | val shift = maxBP.get - el.binaryPoint.get 130 | fromData( 131 | maxBP, 132 | if (shift > 0) el.data << shift else el.data, 133 | Some(maxWidth) 134 | ).asInstanceOf[T] 135 | case nonFp => nonFp 136 | } 137 | } 138 | out.toSeq 139 | } 140 | 141 | private[fixedpoint] def dataAligned(in: FixedPoint*)(implicit sourceInfo: SourceInfo): Seq[FixedPoint] = 142 | dataAligned(in) 143 | 144 | class ImplicitsCls private[fixedpoint] { 145 | 146 | implicit class fromDoubleToLiteral(double: Double) { 147 | def F(binaryPoint: BinaryPoint): FixedPoint = { 148 | FixedPoint.fromDouble(double, UnknownWidth(), binaryPoint) 149 | } 150 | 151 | def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { 152 | FixedPoint.fromDouble(double, width, binaryPoint) 153 | } 154 | } 155 | 156 | implicit class fromBigDecimalToLiteral(bigDecimal: BigDecimal) { 157 | def F(binaryPoint: BinaryPoint): FixedPoint = { 158 | FixedPoint.fromBigDecimal(bigDecimal, UnknownWidth(), binaryPoint) 159 | } 160 | 161 | def F(width: Width, binaryPoint: BinaryPoint): FixedPoint = { 162 | FixedPoint.fromBigDecimal(bigDecimal, width, binaryPoint) 163 | } 164 | } 165 | } 166 | 167 | object Implicits extends ImplicitsCls 168 | 169 | } 170 | 171 | sealed class FixedPoint private[fixedpoint] (width: Width, private var _inferredBinaryPoint: BinaryPoint) 172 | extends Record 173 | with OpaqueType 174 | with Num[FixedPoint] 175 | with HasBinaryPoint { 176 | if (binaryPoint.known) require(binaryPoint.get >= 0, "Negative binary point is not supported") 177 | private val data: SInt = SInt(width) 178 | val elements: SeqMap[String, SInt] = SeqMap("" -> data) 179 | 180 | def binaryPoint: BinaryPoint = _inferredBinaryPoint 181 | 182 | private def requireKnownBP(message: String = "Unknown binary point is not supported in this operation"): Unit = 183 | if (!binaryPoint.known) throw new ChiselException(message) 184 | 185 | private def additiveOp(that: FixedPoint, f: (SInt, SInt) => SInt)(implicit sourceInfo: SourceInfo): FixedPoint = { 186 | val Seq(_this, _that) = FixedPoint.dataAligned(this, that).map(WireDefault(_)) 187 | FixedPoint.fromData(binaryPoint.max(that.binaryPoint), f(_this.data, _that.data)) 188 | } 189 | 190 | private def comparativeOp(that: FixedPoint, f: (SInt, SInt) => Bool): Bool = { 191 | val Seq(_this, _that) = FixedPoint.dataAligned(this, that).map(WireDefault(_)) 192 | f(_this.data, _that.data) 193 | } 194 | 195 | private def connectOp(that: Data, c: (Data, Data) => Unit)(implicit sourceInfo: SourceInfo): Unit = { 196 | that match { 197 | case that: FixedPoint => 198 | if (binaryPoint.known) { 199 | c(data, that.setBinaryPoint(binaryPoint.get).data) 200 | } else { 201 | if (that.binaryPoint.known) { 202 | this._inferredBinaryPoint = BinaryPoint(that.binaryPoint.get) 203 | } 204 | c(data, that.data) 205 | } 206 | case that @ DontCare => 207 | c(data, that) 208 | case _ => throw new ChiselException(s"Cannot connect ${this} and ${that}") 209 | } 210 | } 211 | 212 | override def do_+(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ + _) 213 | 214 | override def do_-(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ - _) 215 | 216 | def do_+%(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ +% _) 217 | 218 | def do_+&(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ +& _) 219 | 220 | def do_-%(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ -% _) 221 | 222 | def do_-&(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = additiveOp(that, _ -& _) 223 | 224 | def do_unary_-(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, -data) 225 | 226 | def do_unary_-%(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data.unary_-%) 227 | 228 | override def do_*(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = 229 | FixedPoint.fromData(binaryPoint + that.binaryPoint, data * that.data) 230 | 231 | override def do_/(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = 232 | throw new ChiselException(s"division is illegal on FixedPoint types") 233 | 234 | override def do_%(that: FixedPoint)(implicit sourceInfo: SourceInfo): FixedPoint = 235 | throw new ChiselException(s"mod is illegal on FixedPoint types") 236 | 237 | override def do_<(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ < _) 238 | 239 | override def do_<=(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ <= _) 240 | 241 | override def do_>(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ > _) 242 | 243 | override def do_>=(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ >= _) 244 | 245 | override def do_abs(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data.abs) 246 | 247 | def do_floor(implicit sourceInfo: SourceInfo): FixedPoint = { 248 | requireKnownBP() 249 | // Set the fractional part to zeroes 250 | val floored = Cat(data >> binaryPoint.get, 0.U(binaryPoint.get.W.min(width))) 251 | FixedPoint.fromData(binaryPoint, floored, Some(width)) 252 | } 253 | 254 | def do_ceil(implicit sourceInfo: SourceInfo): FixedPoint = { 255 | requireKnownBP() 256 | // Get a number with the fractional part set to ones 257 | val almostOne = ((1 << binaryPoint.get) - 1).S 258 | // Add it to the number and floor it 259 | val ceiled = (this + FixedPoint.fromData(binaryPoint, almostOne)).floor 260 | FixedPoint.fromData(binaryPoint, ceiled, Some(width)) 261 | } 262 | 263 | def do_round(implicit sourceInfo: SourceInfo): FixedPoint = { 264 | requireKnownBP() 265 | // Add 0.5 to the number and then floor it 266 | val rounded = (this + 0.5.F(1.BP)).floor.setBinaryPoint(binaryPoint.get) 267 | FixedPoint.fromData(binaryPoint, rounded, Some(width)) 268 | } 269 | 270 | def do_===(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ === _) 271 | 272 | def do_=/=(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ =/= _) 273 | 274 | def do_!=(that: FixedPoint)(implicit sourceInfo: SourceInfo): Bool = comparativeOp(that, _ =/= _) 275 | 276 | def do_>>(that: Int)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data >> that) 277 | 278 | def do_>>(that: BigInt)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data >> that) 279 | 280 | def do_>>(that: UInt)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data >> that) 281 | 282 | def do_<<(that: Int)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data << that) 283 | 284 | def do_<<(that: BigInt)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data << that) 285 | 286 | def do_<<(that: UInt)(implicit sourceInfo: SourceInfo): FixedPoint = FixedPoint.fromData(binaryPoint, data << that) 287 | 288 | def +%(that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg 289 | 290 | def +&(that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg 291 | 292 | def -%(that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg 293 | 294 | def -&(that: FixedPoint): FixedPoint = macro SourceInfoTransform.thatArg 295 | 296 | def unary_- : FixedPoint = macro SourceInfoTransform.noArg 297 | 298 | def unary_-% : FixedPoint = macro SourceInfoTransform.noArg 299 | 300 | def floor: FixedPoint = macro SourceInfoTransform.noArg 301 | 302 | def ceil: FixedPoint = macro SourceInfoTransform.noArg 303 | 304 | def round: FixedPoint = macro SourceInfoTransform.noArg 305 | 306 | def ===(that: FixedPoint): Bool = macro SourceInfoTransform.thatArg 307 | 308 | def =/=(that: FixedPoint): Bool = macro SourceInfoTransform.thatArg 309 | 310 | def !=(that: FixedPoint): Bool = macro SourceInfoTransform.thatArg 311 | 312 | def >>(that: Int): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 313 | 314 | def >>(that: BigInt): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 315 | 316 | def >>(that: UInt): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 317 | 318 | def <<(that: Int): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 319 | 320 | def <<(that: BigInt): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 321 | 322 | def <<(that: UInt): FixedPoint = macro SourceInfoWhiteboxTransform.thatArg 323 | 324 | override def connect(that: Data)(implicit sourceInfo: SourceInfo): Unit = connectOp(that, _ := _) 325 | 326 | override def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = connectOp(that, _ <> _) 327 | 328 | override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo): Unit = { 329 | this.data := that.asTypeOf(this.data) 330 | } 331 | 332 | def apply(x: BigInt): Bool = data.apply(x) 333 | 334 | def apply(x: Int): Bool = data.apply(x) 335 | 336 | def apply(x: UInt): Bool = data.apply(x) 337 | 338 | def apply(x: Int, y: Int): UInt = data.apply(x, y) 339 | 340 | def apply(x: BigInt, y: BigInt): UInt = data.apply(x, y) 341 | 342 | def extract(x: BigInt): Bool = data.extract(x) 343 | 344 | def extract(x: UInt): Bool = data.extract(x) 345 | 346 | final def asSInt: SInt = data.asSInt 347 | 348 | def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo): FixedPoint = { 349 | requireKnownBP(s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") 350 | FixedPoint.fromData(binaryPoint, data, Some(width)) 351 | } 352 | 353 | def do_setBinaryPoint(that: Int)(implicit sourceInfo: SourceInfo): FixedPoint = { 354 | requireKnownBP(s"cannot set new binary point if current binary point is unknown") 355 | val diff = that - binaryPoint.get 356 | FixedPoint.fromData( 357 | that.BP, 358 | if (diff > 0) data << diff 359 | else if (diff < 0) data >> -diff 360 | else data 361 | ) 362 | } 363 | 364 | final def asFixedPoint(that: BinaryPoint): FixedPoint = macro SourceInfoTransform.thatArg 365 | 366 | def setBinaryPoint(that: Int): FixedPoint = macro SourceInfoTransform.thatArg 367 | 368 | def widthKnown: Boolean = data.widthKnown 369 | 370 | override def litOption: Option[BigInt] = data.litOption 371 | 372 | override def litValue: BigInt = data.litValue 373 | 374 | override def toString: String = { 375 | litToDoubleOption match { 376 | case Some(value) => s"FixedPoint$width$binaryPoint($value)" 377 | case _ => 378 | // Can't use stringAccessor so will have to extract from data field's toString... 379 | val suffix = ".*?([(].*[)])".r 380 | .findFirstMatchIn(data.toString) 381 | .fold("")(_.group(1)) 382 | s"FixedPoint$width$binaryPoint$suffix" 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/ForceElementwiseConnect.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // This file contains the definition of a trait a Bundle/Record needs to extend if it containts FixedPoints 4 | // in order to make connections work properly 5 | 6 | package fixedpoint 7 | 8 | import chisel3.{ChiselException, Data, DontCare, Record} 9 | import chisel3.experimental.SourceInfo 10 | 11 | import scala.reflect.ClassTag 12 | 13 | trait ForceElementwiseConnect[T <: Record] extends Record { 14 | implicit val ct: ClassTag[T] 15 | private def connectOp(that: Data, c: (Data, Data) => Unit)(implicit sourceInfo: SourceInfo): Unit = 16 | that match { 17 | case that: T => 18 | this.elements.zip(that.elements).foreach(x => c(x._1._2, x._2._2)) 19 | case that @ DontCare => 20 | this.elements.foreach(x => c(x._2, that)) 21 | case _ => 22 | throw new ChiselException(s"Cannot connect ${this} and ${that}") 23 | } 24 | 25 | override def connect(that: Data)(implicit sourceInfo: SourceInfo): Unit = connectOp(that, _ := _)(sourceInfo) 26 | 27 | override def bulkConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = connectOp(that, _ <> _)(sourceInfo) 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/package.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.{SInt, UInt} 4 | 5 | package object fixedpoint extends FixedPoint.ImplicitsCls { 6 | 7 | implicit class fromSIntToFixedPoint(sInt: SInt) { 8 | def asFixedPoint(binaryPoint: BinaryPoint): FixedPoint = FixedPoint.fromData(binaryPoint, sInt) 9 | } 10 | 11 | implicit class fromUIntToFixedPoint(uInt: UInt) { 12 | def asFixedPoint(binaryPoint: BinaryPoint): FixedPoint = FixedPoint.fromData(binaryPoint, uInt.asSInt) 13 | } 14 | 15 | implicit class fromIntToBinaryPoint(int: Int) { 16 | def BP: BinaryPoint = BinaryPoint(int) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/shadow/MuxDefs.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // This file contains shadowing definitions of chisel3's Mux objects which specifically handle the case 4 | // of parameters containing FixedPoint objects. 5 | 6 | package fixedpoint.shadow 7 | 8 | import chisel3.{Bits, Bool, Data, EnumType, SourceInfoDoc, UInt} 9 | 10 | object Mux extends SourceInfoDoc { 11 | def apply[T <: Data](cond: Bool, con: T, alt: T): T = { 12 | val Seq(con_processed, alt_processed) = Util.processArgs(Seq(con, alt)) 13 | chisel3.Mux(cond, con_processed, alt_processed) 14 | } 15 | } 16 | 17 | object Mux1H { 18 | def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = chisel3.util.Mux1H(sel, Util.processArgs(in)) 19 | def apply[T <: Data](in: Iterable[(Bool, T)]): T = chisel3.util.Mux1H(Util.processArgs(in)) 20 | def apply[T <: Data](sel: UInt, in: Seq[T]): T = chisel3.util.Mux1H(sel, Util.processArgs(in)) 21 | def apply(sel: UInt, in: UInt): Bool = chisel3.util.Mux1H(sel, in) 22 | } 23 | 24 | ///** Builds a Mux tree under the assumption that multiple select signals 25 | // * can be enabled. Priority is given to the first select signal. 26 | // * 27 | // * @example {{{ 28 | // * val hotValue = chisel3.util.PriorityMux(Seq( 29 | // * io.selector(0) -> 2.U, 30 | // * io.selector(1) -> 4.U, 31 | // * io.selector(2) -> 8.U, 32 | // * io.selector(4) -> 11.U, 33 | // * )) 34 | // * }}} 35 | // * Returns the output of the Mux tree. 36 | // */ 37 | object PriorityMux { 38 | def apply[T <: Data](in: Seq[(Bool, T)]): T = chisel3.util.PriorityMux(Util.processArgs[Bool, T](in)) 39 | def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = chisel3.util.PriorityMux(sel, Util.processArgs(in)) 40 | def apply[T <: Data](sel: Bits, in: Seq[T]): T = chisel3.util.PriorityMux(sel, in) 41 | } 42 | 43 | ///** Creates a cascade of n Muxs to search for a key value. 44 | // * 45 | // * @example {{{ 46 | // * MuxLookup(idx, default, 47 | // * Array(0.U -> a, 1.U -> b)) 48 | // * }}} 49 | // */ 50 | object MuxLookup { 51 | 52 | /** @param key a key to search for 53 | * @param default a default value if nothing is found 54 | * @param mapping a sequence to search of keys and values 55 | * @return the value found or the default if not 56 | */ 57 | def apply[T <: Data](key: UInt, default: T)(mapping: Seq[(UInt, T)]): T = { 58 | val (proc_default, proc_mapping) = Util.processArgs(default, mapping) 59 | chisel3.util.MuxLookup(key, proc_default)(proc_mapping) 60 | } 61 | 62 | def apply[S <: EnumType, T <: Data](key: S, default: T)(mapping: Seq[(S, T)]): T = { 63 | val (proc_default, proc_mapping) = Util.processArgs(default, mapping) 64 | chisel3.util.MuxLookup(key, proc_default)(proc_mapping) 65 | } 66 | } 67 | 68 | ///** Given an association of values to enable signals, returns the first value with an associated 69 | // * high enable signal. 70 | // * 71 | // * @example {{{ 72 | // * MuxCase(default, Array(c1 -> a, c2 -> b)) 73 | // * }}} 74 | // */ 75 | object MuxCase { 76 | 77 | /** @param default the default value if none are enabled 78 | * @param mapping a set of data values with associated enables 79 | * @return the first value in mapping that is enabled 80 | */ 81 | def apply[T <: Data](default: T, mapping: Seq[(Bool, T)]): T = { 82 | val (proc_default, proc_mapping) = Util.processArgs(default, mapping) 83 | chisel3.util.MuxCase(proc_default, proc_mapping) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/scala/fixedpoint/shadow/Util.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | // Utility methods for the shadow package 4 | 5 | package fixedpoint.shadow 6 | 7 | import chisel3.Data 8 | import fixedpoint.FixedPoint 9 | 10 | object Util { 11 | def processArgs[T <: Data](in: Seq[T]): Seq[T] = 12 | FixedPoint.dataAligned(in) 13 | 14 | def processArgs[T <: Data, U <: Data](in: Iterable[(T, U)]): Seq[(T, U)] = 15 | in.map(_._1) 16 | .zip(FixedPoint.dataAligned(in.map(_._2))) 17 | .toSeq 18 | 19 | def processArgs[T <: Data, U <: Data](default: U, in: Seq[(T, U)]): (U, Seq[(T, U)]) = { 20 | val aligned = FixedPoint.dataAligned(default +: in.map(_._2)) 21 | (aligned.head, in.map(_._1).zip(aligned.tail)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/scala/AsTypeOfTester.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.testers.BasicTester 4 | import chisel3._ 5 | import fixedpoint._ 6 | 7 | class AsTypeOfBundleTester extends BasicTester { 8 | class MultiTypeBundle extends Bundle { 9 | val u = UInt(4.W) 10 | val s = SInt(4.W) 11 | val fp = FixedPoint(4.W, 3.BP) 12 | } 13 | 14 | val bun = new MultiTypeBundle 15 | 16 | val bunAsTypeOf = ((4 << 8) + (15 << 4) + (12 << 0)).U.asTypeOf(bun) 17 | 18 | assert(bunAsTypeOf.u === 4.U) 19 | assert(bunAsTypeOf.s === -1.S) 20 | assert(bunAsTypeOf.fp === FixedPoint.fromDouble(-0.5, 4.W, 3.BP)) 21 | 22 | stop() 23 | } 24 | 25 | class AsTypeOfSpec extends ChiselFlatSpec { 26 | behavior.of("asTypeOf") 27 | 28 | it should "work with Bundles containing Bits Types" in { 29 | assertTesterPasses { new AsTypeOfBundleTester } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/scala/BundleLiteralSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3._ 4 | import chisel3.experimental.BundleLiterals._ 5 | import chisel3.testers.BasicTester 6 | import fixedpoint._ 7 | 8 | class BundleLiteralSpec extends ChiselFlatSpec with Utils { 9 | 10 | class LongBundle extends Bundle { 11 | val a = UInt(48.W) 12 | val b = SInt(32.W) 13 | val c = FixedPoint(16.W, 4.BP) 14 | } 15 | 16 | "bundle literals" should "pack" in { 17 | assertTesterPasses { 18 | new BasicTester { 19 | val longBundleLit = 20 | (new LongBundle).Lit(_.a -> 0xdeaddeadbeefL.U, _.b -> (-0x0beef00dL).S(32.W), _.c -> 4.5.F(16.W, 4.BP)) 21 | longBundleLit.litOption should equal( 22 | Some( 23 | (BigInt(0xdeaddeadbeefL) << 48) 24 | + (BigInt(0xffffffffL - 0xbeef00dL + 1) << 16) 25 | + BigInt(72) 26 | ) 27 | ) 28 | chisel3.assert(longBundleLit.asUInt === longBundleLit.litOption.get.U) 29 | 30 | stop() 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/scala/ChiselSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import _root_.logger.Logger 4 | import chisel3._ 5 | import chisel3.stage.{ChiselGeneratorAnnotation, PrintFullStackTraceAnnotation} 6 | import chisel3.testers._ 7 | import circt.stage.{CIRCTTarget, CIRCTTargetAnnotation, ChiselStage} 8 | //import firrtl.annotations.Annotation 9 | //import firrtl.ir.Circuit 10 | //import firrtl.util.BackendCompilationUtilities 11 | //import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation} 12 | import org.scalacheck._ 13 | import org.scalatest._ 14 | import org.scalatest.flatspec.AnyFlatSpec 15 | import org.scalatest.freespec.AnyFreeSpec 16 | import org.scalatest.funspec.AnyFunSpec 17 | import org.scalatest.matchers.should.Matchers 18 | import org.scalatest.propspec.AnyPropSpec 19 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks 20 | 21 | import java.io.{ByteArrayOutputStream, PrintStream} 22 | import scala.reflect.ClassTag 23 | 24 | /** Common utility functions for Chisel unit tests. */ 25 | trait ChiselRunners extends Assertions { 26 | def runTester( 27 | t: => BasicTester, 28 | additionalVResources: Seq[String] = Seq() 29 | // annotations: AnnotationSeq = Seq() 30 | ): Boolean = { 31 | val defaultBackend = chisel3.testers.TesterDriver.defaultBackend 32 | // val hasBackend = TestUtils.containsBackend(annotations) 33 | // val annos: Seq[Annotation] = if (hasBackend) annotations else defaultBackend +: annotations 34 | TesterDriver.execute(() => t, additionalVResources) 35 | } 36 | def assertTesterPasses( 37 | t: => BasicTester, 38 | additionalVResources: Seq[String] = Seq() 39 | // annotations: AnnotationSeq = Seq() 40 | ): Unit = { 41 | assert(runTester(t, additionalVResources /*, annotations*/ )) 42 | } 43 | def assertTesterFails( 44 | t: => BasicTester, 45 | additionalVResources: Seq[String] = Seq(), 46 | annotations: Seq[chisel3.aop.Aspect[_]] = Seq() 47 | ): Unit = { 48 | assert(!runTester(t, additionalVResources /*, annotations*/ )) 49 | } 50 | 51 | def assertKnownWidth(expected: Int)(gen: => Data): Unit = { 52 | assertTesterPasses(new BasicTester { 53 | val x = gen 54 | assert(x.getWidth === expected) 55 | // Sanity check that firrtl doesn't change the width 56 | x := 0.U(0.W).asTypeOf(chiselTypeOf(x)) 57 | val (_, done) = chisel3.util.Counter(true.B, 2) 58 | val ones = if (expected == 0) 0.U(0.W) else -1.S(expected.W).asUInt 59 | when(done) { 60 | chisel3.assert(~(x.asUInt) === ones) 61 | stop() 62 | } 63 | }) 64 | } 65 | 66 | def assertInferredWidth(expected: Int)(gen: => Data): Unit = { 67 | assertTesterPasses(new BasicTester { 68 | val x = gen 69 | assert(!x.isWidthKnown, s"Asserting that width should be inferred yet width is known to Chisel!") 70 | x := 0.U(0.W).asTypeOf(chiselTypeOf(x)) 71 | val (_, done) = chisel3.util.Counter(true.B, 2) 72 | val ones = if (expected == 0) 0.U(0.W) else -1.S(expected.W).asUInt 73 | when(done) { 74 | chisel3.assert(~(x.asUInt) === ones) 75 | stop() 76 | } 77 | }) 78 | } 79 | 80 | /** Compiles a Chisel Module to Verilog 81 | * NOTE: This uses the "test_run_dir" as the default directory for generated code. 82 | * @param t the generator for the module 83 | * @return the Verilog code as a string. 84 | */ 85 | // def compile(t: => RawModule): String = { 86 | // (new ChiselStage) 87 | // .execute( 88 | // Array("--target-dir", BackendCompilationUtilities.createTestDirectory(this.getClass.getSimpleName).toString), 89 | // Seq(ChiselGeneratorAnnotation(() => t), CIRCTTargetAnnotation(CIRCTTarget.SystemVerilog)) 90 | // ) 91 | // .collectFirst { 92 | // case EmittedVerilogCircuitAnnotation(a) => a.value 93 | // } 94 | // .getOrElse(fail("No Verilog circuit was emitted by the FIRRTL compiler!")) 95 | // } 96 | 97 | def elaborateAndGetModule[A <: RawModule](t: => A): A = { 98 | var res: Any = null 99 | ChiselStage.convert { 100 | res = t 101 | res.asInstanceOf[A] 102 | } 103 | res.asInstanceOf[A] 104 | } 105 | 106 | /** Compiles a Chisel Module to FIRRTL 107 | * NOTE: This uses the "test_run_dir" as the default directory for generated code. 108 | * @param t the generator for the module 109 | * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation 110 | */ 111 | // def getFirrtlAndAnnos(t: => RawModule, providedAnnotations: Seq[Annotation] = Nil): (Circuit, Seq[Annotation]) = { 112 | // TestUtils.getChirrtlAndAnnotations(t, providedAnnotations) 113 | // } 114 | } 115 | 116 | /** Spec base class for BDD-style testers. */ 117 | abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers 118 | 119 | /** Spec base class for BDD-style testers. */ 120 | abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers 121 | 122 | /** Spec base class for BDD-style testers. */ 123 | abstract class ChiselFunSpec extends AnyFunSpec with ChiselRunners with Matchers 124 | 125 | /** Spec base class for property-based testers. */ 126 | abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers { 127 | 128 | // Constrain the default number of instances generated for every use of forAll. 129 | implicit override val generatorDrivenConfig: PropertyCheckConfiguration = 130 | PropertyCheckConfiguration(minSuccessful = 8, minSize = 1, sizeRange = 3) 131 | 132 | // Generator for small positive integers. 133 | val smallPosInts = Gen.choose(1, 4) 134 | 135 | // Generator for positive (ascending or descending) ranges. 136 | def posRange: Gen[Range] = for { 137 | dir <- Gen.oneOf(true, false) 138 | step <- Gen.choose(1, 3) 139 | m <- Gen.choose(1, 10) 140 | n <- Gen.choose(1, 10) 141 | } yield { 142 | if (dir) { 143 | Range(m, (m + n) * step, step) 144 | } else { 145 | Range((m + n) * step, m, -step) 146 | } 147 | } 148 | 149 | // Generator for widths considered "safe". 150 | val safeUIntWidth = Gen.choose(1, 30) 151 | 152 | // Generators for integers that fit within "safe" widths. 153 | val safeUInts = Gen.choose(0, (1 << 30)) 154 | 155 | // Generators for vector sizes. 156 | val vecSizes = Gen.choose(0, 4) 157 | 158 | // Generator for string representing an arbitrary integer. 159 | val binaryString = for (i <- Arbitrary.arbitrary[Int]) yield "b" + i.toBinaryString 160 | 161 | // Generator for a sequence of Booleans of size n. 162 | def enSequence(n: Int): Gen[List[Boolean]] = Gen.containerOfN[List, Boolean](n, Gen.oneOf(true, false)) 163 | 164 | // Generator which gives a width w and a list (of size n) of numbers up to w bits. 165 | def safeUIntN(n: Int): Gen[(Int, List[Int])] = for { 166 | w <- smallPosInts 167 | i <- Gen.containerOfN[List, Int](n, Gen.choose(0, (1 << w) - 1)) 168 | } yield (w, i) 169 | 170 | // Generator which gives a width w and a numbers up to w bits. 171 | val safeUInt = for { 172 | w <- smallPosInts 173 | i <- Gen.choose(0, (1 << w) - 1) 174 | } yield (w, i) 175 | 176 | // Generator which gives a width w and a list (of size n) of a pair of numbers up to w bits. 177 | def safeUIntPairN(n: Int): Gen[(Int, List[(Int, Int)])] = for { 178 | w <- smallPosInts 179 | i <- Gen.containerOfN[List, Int](n, Gen.choose(0, (1 << w) - 1)) 180 | j <- Gen.containerOfN[List, Int](n, Gen.choose(0, (1 << w) - 1)) 181 | } yield (w, i.zip(j)) 182 | 183 | // Generator which gives a width w and a pair of numbers up to w bits. 184 | val safeUIntPair = for { 185 | w <- smallPosInts 186 | i <- Gen.choose(0, (1 << w) - 1) 187 | j <- Gen.choose(0, (1 << w) - 1) 188 | } yield (w, i, j) 189 | } 190 | 191 | trait Utils { 192 | 193 | /** Run some Scala thunk and return STDOUT and STDERR as strings. 194 | * @param thunk some Scala code 195 | * @return a tuple containing STDOUT, STDERR, and what the thunk returns 196 | */ 197 | def grabStdOutErr[T](thunk: => T): (String, String, T) = { 198 | val stdout, stderr = new ByteArrayOutputStream() 199 | val ret = scala.Console.withOut(stdout) { scala.Console.withErr(stderr) { thunk } } 200 | (stdout.toString, stderr.toString, ret) 201 | } 202 | 203 | /** Run some Scala thunk and return all logged messages as Strings 204 | * @param thunk some Scala code 205 | * @return a tuple containing LOGGED, and what the thunk returns 206 | */ 207 | def grabLog[T](thunk: => T): (String, T) = { 208 | val baos = new ByteArrayOutputStream() 209 | val stream = new PrintStream(baos, true, "utf-8") 210 | val ret = Logger.makeScope(Nil) { 211 | Logger.setOutput(stream) 212 | thunk 213 | } 214 | (baos.toString, ret) 215 | } 216 | 217 | /** Encodes a System.exit exit code 218 | * @param status the exit code 219 | */ 220 | private case class ExitException(status: Int) extends SecurityException(s"Found a sys.exit with code $status") 221 | 222 | /** A security manager that converts calls to System.exit into [[ExitException]]s by explicitly disabling the ability of 223 | * a thread to actually exit. For more information, see: 224 | * - https://docs.oracle.com/javase/tutorial/essential/environment/security.html 225 | */ 226 | // private class ExceptOnExit extends SecurityManager { 227 | // override def checkPermission(perm: Permission): Unit = {} 228 | // override def checkPermission(perm: Permission, context: Object): Unit = {} 229 | // override def checkExit(status: Int): Unit = { 230 | // super.checkExit(status) 231 | // throw ExitException(status) 232 | // } 233 | // } 234 | 235 | /** Encodes a file that some code tries to write to 236 | * @param the file name 237 | */ 238 | private case class WriteException(file: String) extends SecurityException(s"Tried to write to file $file") 239 | 240 | /** A security manager that converts writes to any file into [[WriteException]]s. 241 | */ 242 | // private class ExceptOnWrite extends SecurityManager { 243 | // override def checkPermission(perm: Permission): Unit = {} 244 | // override def checkPermission(perm: Permission, context: Object): Unit = {} 245 | // override def checkWrite(file: String): Unit = { 246 | // super.checkWrite(file) 247 | // throw WriteException(file) 248 | // } 249 | // } 250 | 251 | /** Run some Scala code (a thunk) in an environment where all System.exit are caught and returned. This avoids a 252 | * situation where a test results in something actually exiting and killing the entire test. This is necessary if you 253 | * want to test a command line program, e.g., the `main` method of [[firrtl.options.Stage Stage]]. 254 | * 255 | * NOTE: THIS WILL NOT WORK IN SITUATIONS WHERE THE THUNK IS CATCHING ALL [[Exception]]s OR [[Throwable]]s, E.G., 256 | * SCOPT. IF THIS IS HAPPENING THIS WILL NOT WORK. REPEAT THIS WILL NOT WORK. 257 | * @param thunk some Scala code 258 | * @return either the output of the thunk (`Right[T]`) or an exit code (`Left[Int]`) 259 | */ 260 | // def catchStatus[T](thunk: => T): Either[Int, T] = { 261 | // try { 262 | // System.setSecurityManager(new ExceptOnExit()) 263 | // Right(thunk) 264 | // } catch { 265 | // case ExitException(a) => Left(a) 266 | // } finally { 267 | // System.setSecurityManager(null) 268 | // } 269 | // } 270 | 271 | /** Run some Scala code (a thunk) in an environment where file writes are caught and the file that a program tries to 272 | * write to is returned. This is useful if you want to test that some thunk either tries to write to a specific file 273 | * or doesn't try to write at all. 274 | */ 275 | // def catchWrites[T](thunk: => T): Either[String, T] = { 276 | // throw new Exception("Do not use, not thread-safe") 277 | // try { 278 | // System.setSecurityManager(new ExceptOnWrite()) 279 | // Right(thunk) 280 | // } catch { 281 | // case WriteException(a) => Left(a) 282 | // } finally { 283 | // System.setSecurityManager(null) 284 | // } 285 | // } 286 | 287 | /** A tester which runs generator and uses an aspect to check the returned object 288 | * @param gen function to generate a Chisel module 289 | * @param f a function to check the Chisel module 290 | * @tparam T the Chisel module class 291 | */ 292 | def aspectTest[T <: RawModule](gen: () => T)(f: T => Unit)(implicit scalaMajorVersion: Int): Unit = { 293 | // Runs chisel stage 294 | def run[T <: RawModule](gen: () => T /*, annotations: AnnotationSeq*/ ): Unit /*AnnotationSeq*/ = { 295 | new ChiselStage().run( 296 | Seq( 297 | ChiselGeneratorAnnotation(gen), 298 | CIRCTTargetAnnotation(CIRCTTarget.CHIRRTL), 299 | PrintFullStackTraceAnnotation 300 | ) // ++ annotations 301 | ) 302 | } 303 | // Creates a wrapping aspect to contain checking function 304 | // case object BuiltAspect extends Aspect[T] { 305 | // override def toAnnotation(top: T): AnnotationSeq = { f(top); Nil } 306 | // } 307 | val currentMajorVersion = scala.util.Properties.versionNumberString.split('.')(1).toInt 308 | if (currentMajorVersion >= scalaMajorVersion) { 309 | run(gen /*, Seq(BuiltAspect)*/ ) 310 | } 311 | } 312 | 313 | /** Run some code and rethrow an exception with a specific type if an exception of that type occurs anywhere in the 314 | * stack trace. 315 | * 316 | * This is useful for "extracting" one exception that may be wrapped by other exceptions. 317 | * 318 | * Example usage: 319 | * {{{ 320 | * a [ChiselException] should be thrownBy extractCause[ChiselException] { /* ... */ } 321 | * }}} 322 | * 323 | * @param thunk some code to run 324 | * @tparam A the type of the exception to extract 325 | * @return nothing 326 | */ 327 | def extractCause[A <: Throwable: ClassTag](thunk: => Any): Unit = { 328 | def unrollCauses(a: Throwable): Seq[Throwable] = a match { 329 | case null => Seq.empty 330 | case _ => a +: unrollCauses(a.getCause) 331 | } 332 | 333 | val exceptions: Seq[_ <: Throwable] = 334 | try { 335 | thunk 336 | Seq.empty 337 | } catch { 338 | case a: Throwable => unrollCauses(a) 339 | } 340 | 341 | exceptions.collectFirst { case a: A => a } match { 342 | case Some(a) => throw a 343 | case None => 344 | exceptions match { 345 | case Nil => () 346 | case h :: t => throw h 347 | } 348 | } 349 | 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/test/scala/ConnectSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.experimental.Analog 4 | import circt.stage.ChiselStage 5 | import chisel3.testers.BasicTester 6 | import chisel3._ 7 | import fixedpoint._ 8 | 9 | class CrossConnects(inType: Data, outType: Data) extends Module { 10 | val io = IO(new Bundle { 11 | val in = Input(inType) 12 | val out = Output(outType) 13 | }) 14 | io.out := io.in 15 | } 16 | 17 | class CrossConnectTester(inType: Data, outType: Data) extends BasicTester { 18 | val dut = Module(new CrossConnects(inType, outType)) 19 | dut.io := DontCare 20 | stop() 21 | } 22 | 23 | class ConnectSpec extends ChiselPropSpec with Utils { 24 | property("SInt := FixedPoint should fail") { 25 | intercept[ChiselException] { 26 | extractCause[ChiselException] { 27 | ChiselStage.emitCHIRRTL { new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } 28 | } 29 | } 30 | } 31 | property("UInt := FixedPoint should fail") { 32 | intercept[ChiselException] { 33 | extractCause[ChiselException] { 34 | ChiselStage.emitCHIRRTL { new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } 35 | } 36 | } 37 | } 38 | property("FixedPoint := FixedPoint should succeed") { 39 | assertTesterPasses { new CrossConnectTester(FixedPoint(16.W, 8.BP), FixedPoint(16.W, 8.BP)) } 40 | assertTesterPasses { new CrossConnectTester(FixedPoint(2.W, 14.BP), FixedPoint(8.W, 6.BP)) } 41 | } 42 | property("FixedPoint := SInt should fail") { 43 | intercept[ChiselException] { 44 | extractCause[ChiselException] { 45 | ChiselStage.emitCHIRRTL { new CrossConnectTester(SInt(16.W), FixedPoint(16.W, 8.BP)) } 46 | } 47 | } 48 | } 49 | property("FixedPoint := UInt should fail") { 50 | intercept[ChiselException] { 51 | extractCause[ChiselException] { 52 | ChiselStage.emitCHIRRTL { new CrossConnectTester(UInt(16.W), FixedPoint(16.W, 8.BP)) } 53 | } 54 | } 55 | } 56 | property("Analog := FixedPoint should fail") { 57 | intercept[ChiselException] { 58 | extractCause[ChiselException] { 59 | ChiselStage.emitCHIRRTL { new CrossConnectTester(Analog(16.W), FixedPoint(16.W, 8.BP)) } 60 | } 61 | } 62 | } 63 | property("FixedPoint := Analog should fail") { 64 | intercept[ChiselException] { 65 | extractCause[ChiselException] { 66 | ChiselStage.emitCHIRRTL { new CrossConnectTester(FixedPoint(16.W, 8.BP), Analog(16.W)) } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/scala/DataEqualitySpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3._ 4 | import chisel3.experimental.BundleLiterals._ 5 | import chisel3.testers.BasicTester 6 | import fixedpoint._ 7 | 8 | class EqualityModule(lhsGen: => Data, rhsGen: => Data) extends Module { 9 | val out = IO(Output(Bool())) 10 | 11 | val lhs = lhsGen 12 | val rhs = rhsGen 13 | 14 | out := lhs === rhs 15 | } 16 | 17 | class EqualityTester(lhsGen: => Data, rhsGen: => Data) extends BasicTester { 18 | val module = Module(new EqualityModule(lhsGen, rhsGen)) 19 | 20 | assert(module.out) 21 | 22 | stop() 23 | } 24 | 25 | class DataEqualitySpec extends ChiselFlatSpec with Utils { 26 | object MyEnum extends ChiselEnum { 27 | val sA, sB = Value 28 | } 29 | 30 | class MyBundle extends Bundle { 31 | val a = UInt(8.W) 32 | val b = Bool() 33 | val c = MyEnum() 34 | } 35 | 36 | class LongBundle extends Bundle { 37 | val a = UInt(48.W) 38 | val b = SInt(32.W) 39 | val c = FixedPoint(16.W, 4.BP) 40 | } 41 | 42 | class RuntimeSensitiveBundle(gen: => Bundle) extends Bundle { 43 | val a = UInt(8.W) 44 | val b: Bundle = gen 45 | } 46 | 47 | behavior.of("FixedPoint === FixedPoint") 48 | it should "pass with equal values" in { 49 | assertTesterPasses { 50 | new EqualityTester(4.5.F(16.W, 4.BP), 4.5.F(16.W, 4.BP)) 51 | } 52 | } 53 | it should "fail with differing values" in { 54 | assertTesterFails { 55 | new EqualityTester(4.5.F(16.W, 4.BP), 4.6.F(16.W, 4.BP)) 56 | } 57 | } 58 | 59 | behavior.of("Bundle === Bundle") 60 | it should "throw a ChiselException with differing runtime types" in { 61 | (the[ChiselException] thrownBy extractCause[ChiselException] { 62 | assertTesterFails { 63 | new EqualityTester( 64 | (new RuntimeSensitiveBundle(new MyBundle)).Lit( 65 | _.a -> 1.U, 66 | _.b -> (new MyBundle).Lit( 67 | _.a -> 42.U, 68 | _.b -> false.B, 69 | _.c -> MyEnum.sB 70 | ) 71 | ), 72 | (new RuntimeSensitiveBundle(new LongBundle)).Lit( 73 | _.a -> 1.U, 74 | _.b -> (new LongBundle).Lit( 75 | _.a -> 42.U, 76 | _.b -> 0.S, 77 | _.c -> 4.5.F(16.W, 4.BP) 78 | ) 79 | ) 80 | ) 81 | } 82 | }).getMessage should include("Runtime types differ") 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/scala/DataPrint.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.experimental.BundleLiterals._ 4 | import circt.stage.ChiselStage 5 | import chisel3._ 6 | import fixedpoint._ 7 | import org.scalatest.matchers.should.Matchers 8 | 9 | class DataPrintSpec extends ChiselFlatSpec with Matchers { 10 | object EnumTest extends ChiselEnum { 11 | val sNone, sOne, sTwo = Value 12 | } 13 | 14 | class PartialBundleTest extends Bundle { 15 | val a = UInt(8.W) 16 | val b = Bool() 17 | val c = SInt(8.W) 18 | val e = FixedPoint(5.W, 3.BP) 19 | val f = EnumTest.Type() 20 | } 21 | 22 | "Data types" should "have a meaningful string representation" in { 23 | ChiselStage.emitCHIRRTL { 24 | new RawModule { 25 | FixedPoint(5.W, 3.BP).toString should be("FixedPoint<5><<3>>") 26 | } 27 | } 28 | } 29 | 30 | "Literals" should "have a meaningful string representation" in { 31 | ChiselStage.emitCHIRRTL { 32 | new RawModule { 33 | 2.25.F(6.W, 2.BP).toString should be("FixedPoint<6><<2>>(2.25)") 34 | (-2.25).F(6.W, 2.BP).toString should be("FixedPoint<6><<2>>(-2.25)") 35 | (new PartialBundleTest).Lit().toString should be( 36 | "PartialBundleTest(a=UInt<8>(DontCare), b=Bool(DontCare), c=SInt<8>(DontCare), e=FixedPoint<5><<3>>(DontCare), f=EnumTest(DontCare))" 37 | ) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/scala/FixedPointSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import circt.stage.ChiselStage 4 | import chisel3.testers.BasicTester 5 | import chisel3.{Mux => _, _} 6 | import fixedpoint._ 7 | import fixedpoint.shadow.Mux 8 | import org.scalatest.flatspec.AnyFlatSpec 9 | import org.scalatest.matchers.should.Matchers 10 | 11 | class FixedPointLiteralSpec extends AnyFlatSpec with Matchers { 12 | behavior.of("fixed point utilities") 13 | 14 | they should "allow conversion between doubles and the bigints needed to represent them" in { 15 | val initialDouble = 0.125 16 | val bigInt = FixedPoint.toBigInt(initialDouble, 4) 17 | val finalDouble = FixedPoint.toDouble(bigInt, 4) 18 | 19 | initialDouble should be(finalDouble) 20 | } 21 | 22 | they should "have their literals support to double and to BigDecimal" in { 23 | val d = -7.125 24 | val lit1 = d.F(3.BP) 25 | lit1.litToDouble should be(d) 26 | 27 | val d2 = BigDecimal("1232123213131123.125") 28 | val lit2 = d2.F(3.BP) 29 | lit2.litToBigDecimal should be(d2) 30 | 31 | // Numbers that are too big will throw exception 32 | intercept[ChiselException] { 33 | lit2.litToDouble 34 | } 35 | } 36 | } 37 | 38 | //noinspection TypeAnnotation,EmptyParenMethodAccessedAsParameterless 39 | class FixedPointFromBitsTester extends BasicTester { 40 | val uint = 3.U(4.W) 41 | val sint = (-3).S 42 | 43 | val fp = FixedPoint.fromDouble(3.0, 4.W, 0.BP) 44 | val fp_tpe = FixedPoint(4.W, 1.BP) 45 | val uint_result = FixedPoint.fromDouble(1.5, 4.W, 1.BP) 46 | val sint_result = FixedPoint.fromDouble(-1.5, 4.W, 1.BP) 47 | val fp_result = FixedPoint.fromDouble(1.5, 4.W, 1.BP) 48 | 49 | val uint2fp = uint.asTypeOf(fp_tpe) 50 | val sint2fp = sint.asTypeOf(fp_tpe) 51 | val fp2fp = fp.asTypeOf(fp_tpe) 52 | 53 | val uintToFp = uint.asFixedPoint(1.BP) 54 | val sintToFp = sint.asFixedPoint(1.BP) 55 | val fpToFp = fp.asFixedPoint(1.BP) 56 | 57 | val negativefp = (-3.5).F(4.BP) 58 | val positivefp = 3.5.F(4.BP) 59 | 60 | assert(-positivefp === negativefp) 61 | assert(positivefp === -negativefp) 62 | 63 | assert(uint2fp === uint_result) 64 | assert(sint2fp === sint_result) 65 | assert(fp2fp === fp_result) 66 | 67 | assert(uintToFp === uint_result) 68 | assert(sintToFp === sint_result) 69 | assert(fpToFp === fp_result) 70 | 71 | assert(positivefp.abs === positivefp) 72 | assert(negativefp.abs === positivefp) 73 | assert(negativefp.abs =/= negativefp) 74 | 75 | val f1bp5 = 1.5.F(1.BP) 76 | val f6bp0 = 6.0.F(0.BP) 77 | val f6bp2 = 6.0.F(2.BP) 78 | 79 | val f1bp5shiftleft2 = Wire(FixedPoint(UnknownWidth(), BinaryPoint())) 80 | val f6bp0shiftright2 = Wire(FixedPoint(UnknownWidth(), BinaryPoint())) 81 | val f6bp2shiftright2 = Wire(FixedPoint(UnknownWidth(), BinaryPoint())) 82 | 83 | f1bp5shiftleft2 := f1bp5 << 2 84 | f6bp0shiftright2 := f6bp0 >> 2 85 | f6bp2shiftright2 := f6bp2 >> 2 86 | 87 | assert(f1bp5shiftleft2 === f6bp0) 88 | assert(f1bp5shiftleft2 === 6.0.F(8.BP)) 89 | 90 | // shifting does not move binary point, so in first case below one bit is lost in shift 91 | assert(f6bp0shiftright2 === 1.0.F(0.BP)) 92 | assert(f6bp2shiftright2 === 1.5.F(2.BP)) 93 | 94 | stop() 95 | } 96 | 97 | class FixedPointMuxTester extends BasicTester { 98 | val largeWidthLowPrecision = 6.0.F(4.W, 0.BP) 99 | val smallWidthHighPrecision = 0.25.F(2.W, 2.BP) 100 | val unknownWidthLowPrecision = 6.0.F(0.BP) 101 | val unknownFixed = Wire(FixedPoint()) 102 | unknownFixed := smallWidthHighPrecision 103 | 104 | assert(Mux(true.B, largeWidthLowPrecision, smallWidthHighPrecision) === 6.0.F(0.BP)) 105 | assert(Mux(false.B, largeWidthLowPrecision, smallWidthHighPrecision) === 0.25.F(2.BP)) 106 | assert(Mux(false.B, largeWidthLowPrecision, unknownFixed) === 0.25.F(2.BP)) 107 | assert(Mux(true.B, unknownWidthLowPrecision, smallWidthHighPrecision) === 6.0.F(0.BP)) 108 | 109 | stop() 110 | } 111 | 112 | class SBP extends Module { 113 | val io = IO(new Bundle { 114 | val in = Input(FixedPoint(6.W, 2.BP)) 115 | val out = Output(FixedPoint(4.W, 0.BP)) 116 | }) 117 | io.out := io.in.setBinaryPoint(0) 118 | } 119 | 120 | class SBPTester extends BasicTester { 121 | val dut = Module(new SBP) 122 | dut.io.in := 3.75.F(2.BP) 123 | 124 | assert(dut.io.out === 3.0.F(0.BP)) 125 | 126 | val test = Wire(FixedPoint(10.W, 5.BP)) 127 | // Initialize test, avoiding a "Reference test is not fully initialized" error from firrtl. 128 | test := 0.0.F(5.BP) 129 | val q = test.setBinaryPoint(18) 130 | assert(q.getWidth.U === 23.U) 131 | 132 | stop() 133 | } 134 | 135 | class NegativeShift(t: => Bits) extends Module { 136 | val io = IO(new Bundle {}) 137 | Reg(t) >> -1 138 | } 139 | 140 | class FixedPointLitExtractTester extends BasicTester { 141 | assert(-4.75.F(2.BP)(1) === false.B) 142 | assert(-4.75.F(2.BP)(2) === true.B) 143 | assert(-4.75.F(2.BP)(100) === true.B) 144 | assert(-4.75.F(2.BP)(3, 0) === "b1101".U) 145 | assert(-4.75.F(2.BP)(9, 0) === "b1111101101".U) 146 | 147 | assert(-4.75.F(6.W, 2.BP)(1) === false.B) 148 | assert(-4.75.F(6.W, 2.BP)(2) === true.B) 149 | assert(-4.75.F(6.W, 2.BP)(100) === true.B) 150 | assert(-4.75.F(6.W, 2.BP)(3, 0) === "b1101".U) 151 | assert(-4.75.F(6.W, 2.BP)(9, 0) === "b1111101101".U) 152 | 153 | assert(-0.125.F(2.W, 4.BP)(0) === false.B) 154 | assert(-0.125.F(2.W, 4.BP)(1) === true.B) 155 | assert(-0.125.F(2.W, 4.BP)(100) === true.B) 156 | assert(-0.125.F(2.W, 4.BP)(1, 0) === "b10".U) 157 | assert(0.0625.F(2.W, 4.BP)(0) === true.B) 158 | assert(0.0625.F(2.W, 4.BP)(1) === false.B) 159 | assert(0.0625.F(2.W, 4.BP)(100) === false.B) 160 | assert(0.0625.F(2.W, 4.BP)(1, 0) === "b01".U) 161 | stop() 162 | } 163 | 164 | class FixedPointUnaryFuncTester(f: FixedPoint => FixedPoint, inExpected: Seq[(FixedPoint, FixedPoint)]) 165 | extends BasicTester { 166 | inExpected.foreach { 167 | case (in, expected) => 168 | val out = f(in) 169 | assert(out === expected, cf"Wrong value: in=${in}; out=${out}; expected=${expected}") 170 | assert(out.widthOption == in.widthOption, f"Width changed: in=${in}; out=${out}") 171 | assert(out.binaryPoint == in.binaryPoint, f"Binary point changed: in=${in}; out=${out}") 172 | } 173 | stop() 174 | } 175 | 176 | class FixedPointFloorTester 177 | extends FixedPointUnaryFuncTester( 178 | _.floor, 179 | Seq( 180 | -4.75.F(8.W, 2.BP) -> -5.0.F(8.W, 2.BP), 181 | 55.5.F(8.W, 2.BP) -> 55.0.F(8.W, 2.BP), 182 | -4.0.F(2.BP) -> -4.0.F(2.BP), 183 | 0.125.F(8.W, 4.BP) -> 0.0.F(8.W, 4.BP), 184 | 3.0.F(0.BP) -> 3.0.F(0.BP), 185 | // Overflow to zero when binaryPoint >= width 186 | 0.25.F(2.W, 2.BP) -> 0.F(0.BP), 187 | -0.5.F(2.W, 2.BP) -> 0.F(0.BP), 188 | 0.0625.F(2.W, 4.BP) -> 0.F(0.BP), 189 | -0.125.F(2.W, 4.BP) -> 0.F(0.BP) 190 | ) 191 | ) 192 | 193 | class FixedPointCeilTester 194 | extends FixedPointUnaryFuncTester( 195 | _.ceil, 196 | Seq( 197 | -4.75.F(8.W, 2.BP) -> -4.0.F(8.W, 2.BP), 198 | 55.5.F(8.W, 2.BP) -> 56.0.F(8.W, 2.BP), 199 | -4.0.F(2.BP) -> -4.0.F(2.BP), 200 | 0.125.F(8.W, 4.BP) -> 1.0.F(8.W, 4.BP), 201 | 3.0.F(0.BP) -> 3.0.F(0.BP), 202 | // Overflow to zero when binaryPoint >= width 203 | 0.25.F(2.W, 2.BP) -> 0.F(0.BP), 204 | -0.5.F(2.W, 2.BP) -> 0.F(0.BP), 205 | 0.0625.F(2.W, 4.BP) -> 0.F(0.BP), 206 | -0.125.F(2.W, 4.BP) -> 0.F(0.BP) 207 | ) 208 | ) 209 | 210 | class FixedPointRoundTester 211 | extends FixedPointUnaryFuncTester( 212 | _.round, 213 | Seq( 214 | -4.75.F(8.W, 2.BP) -> -5.0.F(8.W, 2.BP), 215 | 25.5.F(8.W, 2.BP) -> 26.0.F(8.W, 2.BP), 216 | -4.0.F(2.BP) -> -4.0.F(2.BP), 217 | 0.125.F(8.W, 3.BP) -> 0.0.F(8.W, 3.BP), 218 | 3.0.F(0.BP) -> 3.0.F(0.BP), 219 | // Overflow to zero when binaryPoint >= width 220 | 0.25.F(2.W, 2.BP) -> 0.F(0.BP), 221 | -0.5.F(2.W, 2.BP) -> 0.F(0.BP), 222 | 0.0625.F(2.W, 4.BP) -> 0.F(0.BP), 223 | -0.125.F(2.W, 4.BP) -> 0.F(0.BP) 224 | ) 225 | ) 226 | 227 | class FixedPointSpec extends ChiselPropSpec with Utils { 228 | property("should allow set binary point") { 229 | assertTesterPasses { new SBPTester } 230 | } 231 | property("should allow fromBits") { 232 | assertTesterPasses { new FixedPointFromBitsTester } 233 | } 234 | property("should mux different widths and binary points") { 235 | assertTesterPasses { new FixedPointMuxTester } 236 | } 237 | property("Negative shift amounts are invalid") { 238 | a[ChiselException] should be thrownBy extractCause[ChiselException] { 239 | ChiselStage.emitCHIRRTL(new NegativeShift(FixedPoint(1.W, 0.BP).asSInt)) 240 | } 241 | } 242 | property("Bit extraction on literals should work for all non-negative indices") { 243 | assertTesterPasses(new FixedPointLitExtractTester) 244 | } 245 | 246 | property("Floor operation works") { 247 | assertTesterPasses { new FixedPointFloorTester } 248 | } 249 | 250 | property("Ceil operation works") { 251 | assertTesterPasses { new FixedPointCeilTester } 252 | } 253 | 254 | property("Round operation works") { 255 | assertTesterPasses { new FixedPointRoundTester } 256 | } 257 | 258 | property("Negative binary point is invalid") { 259 | assertThrows[IllegalArgumentException](new BasicTester { 2.F((-1).BP) }) 260 | assertThrows[IllegalArgumentException](new BasicTester { 1.F(0.BP).setBinaryPoint(-1) }) 261 | assertThrows[IllegalArgumentException](new BasicTester { FixedPoint(4.W, (-2).BP) }) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/test/scala/LiteralExtractorSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.experimental.BundleLiterals._ 4 | import chisel3.testers.BasicTester 5 | import chisel3._ 6 | import fixedpoint._ 7 | 8 | class LiteralExtractorSpec extends ChiselFlatSpec { 9 | "litValue" should "return the literal value" in { 10 | assert(1.25.F(2.BP).litValue === BigInt("101", 2)) 11 | assert(2.25.F(2.BP).litValue === BigInt("1001", 2)) 12 | assert(0.0625.F(2.W, 4.BP).litValue === BigInt("01", 2)) 13 | 14 | assert(-1.25.F(2.BP).litValue === BigInt("-101", 2)) 15 | assert(-2.25.F(2.BP).litValue === BigInt("-1001", 2)) 16 | assert(-0.0625.F(2.W, 4.BP).litValue === BigInt("-01", 2)) 17 | } 18 | 19 | "litToDouble" should "return the literal value" in { 20 | assert(1.25.F(2.BP).litToDouble == 1.25) 21 | assert(2.25.F(2.BP).litToDouble == 2.25) 22 | assert(0.0625.F(2.W, 4.BP).litToDouble == 0.0625) 23 | 24 | assert(-1.25.F(2.BP).litToDouble == -1.25) 25 | assert(-2.25.F(2.BP).litToDouble == -2.25) 26 | assert(-0.0625.F(2.W, 4.BP).litToDouble == -0.0625) 27 | 28 | // test rounding 29 | assert(1.24.F(1.BP).litToDouble == 1.0) 30 | assert(1.25.F(1.BP).litToDouble == 1.5) 31 | assert(0.0624.F(2.W, 3.BP).litToDouble == 0.0) 32 | assert(0.0625.F(2.W, 3.BP).litToDouble == 0.125) 33 | } 34 | 35 | "doubles and big decimals" should "round trip properly" in { 36 | intercept[ChiselException] { 37 | NumBP.toBigDecimal(BigInt("1" * 109, 2), 0.BP) // this only works if number takes less than 109 bits 38 | } 39 | 40 | intercept[ChiselException] { 41 | NumBP.toDouble(BigInt("1" * 54, 2), 0.BP) // this only works if number takes less than 54 bits 42 | } 43 | 44 | val bigInt108 = BigInt("1" * 108, 2) 45 | val bigDecimal = Num.toBigDecimal(bigInt108, 2) 46 | 47 | val bigIntFromBigDecimal = Num.toBigInt(bigDecimal, 2) 48 | 49 | bigIntFromBigDecimal should be(bigInt108) 50 | 51 | val bigInt53 = BigInt("1" * 53, 2) 52 | 53 | val double = Num.toDouble(bigInt53, 2) 54 | 55 | val bigIntFromDouble = Num.toBigInt(double, 2) 56 | 57 | bigIntFromDouble should be(bigInt53) 58 | } 59 | 60 | "literals declared outside a builder context" should "compare with those inside builder context" in { 61 | class InsideBundle extends Bundle { 62 | val x = FixedPoint(8.W, 4.BP) 63 | } 64 | 65 | class LitInsideOutsideTester(outsideLiteral: InsideBundle) extends BasicTester { 66 | val insideLiteral = (new InsideBundle).Lit(_.x -> 6.125.F(8.W, 4.BP)) 67 | 68 | // the following errors with "assertion failed" 69 | 70 | println(outsideLiteral === insideLiteral) 71 | // chisel3.assert(outsideLiteral === insideLiteral) 72 | 73 | // the following lines of code error 74 | // with "chisel3.internal.BundleLitBinding cannot be cast to chisel3.internal.ElementLitBinding" 75 | 76 | chisel3.assert(outsideLiteral.x === insideLiteral.x) 77 | chisel3.assert(outsideLiteral.x === 6.125.F(4.BP)) 78 | 79 | stop() 80 | } 81 | 82 | val outsideLiteral = (new InsideBundle).Lit(_.x -> 6.125.F(8.W, 4.BP)) 83 | assertTesterPasses { new LitInsideOutsideTester(outsideLiteral) } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/scala/OneHotMuxSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.ChiselException 4 | import chisel3.testers.BasicTester 5 | import chisel3._ 6 | import fixedpoint._ 7 | import fixedpoint.shadow.Mux1H 8 | import org.scalatest.freespec.AnyFreeSpec 9 | import org.scalatest.matchers.should.Matchers 10 | 11 | class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners { 12 | "simple one hot mux with fixed point should work" in { 13 | assertTesterPasses(new FixedPointOneHotTester) 14 | } 15 | "simple one hot mux with all same fixed point should work" in { 16 | assertTesterPasses(new AllSameFixedPointOneHotTester) 17 | } 18 | "simple one hot mux with all same parameterized aggregates containing fixed values should work" in { 19 | assertTesterPasses(new ParameterizedAggregateOneHotTester) 20 | } 21 | "simple one hot mux with all aggregates containing inferred width fixed values should NOT work" in { 22 | intercept[ChiselException] { 23 | assertTesterPasses(new InferredWidthAggregateOneHotTester) 24 | } 25 | } 26 | "simple one hot mux with all fixed width bundles but with different bundles should Not work" in { 27 | intercept[IllegalArgumentException] { 28 | assertTesterPasses(new DifferentBundleOneHotTester) 29 | } 30 | } 31 | } 32 | 33 | class FixedPointOneHotTester extends BasicTester { 34 | val out = Wire(FixedPoint(8.W, 4.BP)) 35 | 36 | out := Mux1H( 37 | Seq( 38 | false.B -> (-1.5).F(8.W, 1.BP), 39 | true.B -> (-2.25).F(8.W, 2.BP), 40 | false.B -> (-4.125).F(8.W, 3.BP), 41 | false.B -> (-11.625).F(8.W, 3.BP) 42 | ) 43 | ) 44 | 45 | assert(out === (-2.25).F(4.BP)) 46 | 47 | stop() 48 | } 49 | 50 | class AllSameFixedPointOneHotTester extends BasicTester { 51 | val out = Wire(FixedPoint(12.W, 3.BP)) 52 | 53 | out := Mux1H( 54 | Seq( 55 | false.B -> (-1.5).F(12.W, 3.BP), 56 | true.B -> (-2.25).F(12.W, 3.BP), 57 | false.B -> (-4.125).F(12.W, 3.BP), 58 | false.B -> (-11.625).F(12.W, 3.BP) 59 | ) 60 | ) 61 | 62 | assert(out === (-2.25).F(14.W, 4.BP)) 63 | 64 | stop() 65 | } 66 | 67 | class Agg1 extends Bundle { 68 | val v = Vec(2, FixedPoint(8.W, 4.BP)) 69 | val a = new Bundle { 70 | val f1 = FixedPoint(7.W, 3.BP) 71 | val f2 = FixedPoint(9.W, 5.BP) 72 | } 73 | } 74 | 75 | object Agg1 extends HasMakeLit[Agg1] { 76 | def makeLit(n: Int): Agg1 = { 77 | val x = n.toDouble / 4.0 78 | val (d: Double, e: Double, f: Double, g: Double) = (x, x * 2.0, x * 3.0, x * 4.0) 79 | 80 | val w = Wire(new Agg1) 81 | w.v(0) := d.F(4.BP) 82 | w.v(1) := e.F(4.BP) 83 | w.a.f1 := f.F(3.BP) 84 | w.a.f2 := g.F(5.BP) 85 | w 86 | } 87 | } 88 | class Agg2 extends Bundle { 89 | val v = Vec(2, FixedPoint(8.W, 4.BP)) 90 | val a = new Bundle { 91 | val f1 = FixedPoint(7.W, 3.BP) 92 | val f2 = FixedPoint(9.W, 5.BP) 93 | } 94 | } 95 | 96 | object Agg2 extends HasMakeLit[Agg2] { 97 | def makeLit(n: Int): Agg2 = { 98 | val x = n.toDouble / 4.0 99 | val (d: Double, e: Double, f: Double, g: Double) = (x, x * 2.0, x * 3.0, x * 4.0) 100 | 101 | val w = Wire(new Agg2) 102 | w.v(0) := d.F(4.BP) 103 | w.v(1) := e.F(4.BP) 104 | w.a.f1 := f.F(3.BP) 105 | w.a.f2 := g.F(5.BP) 106 | w 107 | } 108 | } 109 | 110 | class ParameterizedAggregateOneHotTester extends BasicTester { 111 | val values = (0 until 4).map { n => Agg1.makeLit(n) } 112 | for ((v, i) <- values.zipWithIndex) { 113 | val dut = Module(new ParameterizedAggregateOneHot(Agg1, new Agg1)) 114 | dut.io.selectors := (1 << i).U(4.W).asBools 115 | 116 | assert(dut.io.out.asUInt === values(i).asUInt) 117 | } 118 | 119 | stop() 120 | } 121 | 122 | trait HasMakeLit[T] { 123 | def makeLit(n: Int): T 124 | } 125 | 126 | class ParameterizedAggregateOneHot[T <: Data](valGen: HasMakeLit[T], outGen: T) extends Module { 127 | val io = IO(new Bundle { 128 | val selectors = Input(Vec(4, Bool())) 129 | val out = Output(outGen) 130 | }) 131 | 132 | val values = (0 until 4).map { n => valGen.makeLit(n) } 133 | val terms = io.selectors.zip(values) 134 | io.out := Mux1H(terms) 135 | } 136 | 137 | class Bundle1 extends Bundle { 138 | val a = FixedPoint() 139 | val b = new Bundle { 140 | val c = FixedPoint() 141 | } 142 | } 143 | 144 | class InferredWidthAggregateOneHotTester extends BasicTester { 145 | val b0 = Wire(new Bundle1) 146 | b0.a := -0.25.F(2.BP) 147 | b0.b.c := -0.125.F(3.BP) 148 | 149 | val b1 = Wire(new Bundle1) 150 | b1.a := -0.0625.F(3.BP) 151 | b1.b.c := -0.03125.F(4.BP) 152 | 153 | val b2 = Wire(new Bundle1) 154 | b2.a := -0.015625.F(5.BP) 155 | b2.b.c := -0.0078125.F(6.BP) 156 | 157 | val b3 = Wire(new Bundle1) 158 | b3.a := -0.0078125.F(7.BP) 159 | b3.b.c := -0.00390625.F(8.BP) 160 | 161 | val o1 = Mux1H( 162 | Seq( 163 | false.B -> b0, 164 | false.B -> b1, 165 | true.B -> b2, 166 | false.B -> b3 167 | ) 168 | ) 169 | 170 | assert(o1.a === -0.015625.F(5.BP)) 171 | assert(o1.b.c === -0.0078125.F(6.BP)) 172 | 173 | val o2 = Mux1H( 174 | Seq( 175 | false.B -> b0, 176 | true.B -> b1, 177 | false.B -> b2, 178 | false.B -> b3 179 | ) 180 | ) 181 | 182 | assert(o2.a === -0.0625.F(3.BP)) 183 | assert(o2.b.c === -0.03125.F(4.BP)) 184 | 185 | stop() 186 | } 187 | 188 | class Bundle2 extends Bundle { 189 | val a = FixedPoint(10.W, 4.BP) 190 | val b = new Bundle { 191 | val c = FixedPoint(10.W, 4.BP) 192 | } 193 | } 194 | 195 | class Bundle3 extends Bundle { 196 | val a = FixedPoint(10.W, 4.BP) 197 | val b = new Bundle { 198 | val c = FixedPoint(10.W, 4.BP) 199 | } 200 | } 201 | 202 | class DifferentBundleOneHotTester extends BasicTester { 203 | val b0 = Wire(new Bundle2) 204 | b0.a := -0.25.F(2.BP) 205 | b0.b.c := -0.125.F(3.BP) 206 | 207 | val b1 = Wire(new Bundle2) 208 | b1.a := -0.0625.F(3.BP) 209 | b1.b.c := -0.03125.F(4.BP) 210 | 211 | val b2 = Wire(new Bundle3) 212 | b2.a := -0.015625.F(5.BP) 213 | b2.b.c := -0.0078125.F(6.BP) 214 | 215 | val b3 = Wire(new Bundle3) 216 | b3.a := -0.0078125.F(7.BP) 217 | b3.b.c := -0.00390625.F(8.BP) 218 | 219 | val o1 = Mux1H( 220 | Seq( 221 | false.B -> b0, 222 | false.B -> b1, 223 | true.B -> b2, 224 | false.B -> b3 225 | ) 226 | ) 227 | 228 | stop() 229 | } 230 | -------------------------------------------------------------------------------- /src/test/scala/VecLiteralSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | import chisel3.experimental.VecLiterals._ 4 | import chisel3.testers.BasicTester 5 | import chisel3._ 6 | import fixedpoint._ 7 | 8 | import scala.language.reflectiveCalls 9 | 10 | class VecLiteralSpec extends ChiselFreeSpec with Utils { 11 | //TODO: decide what behavior here should be 12 | "This doesn't work should it" ignore { 13 | assertTesterPasses { 14 | new BasicTester { 15 | def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) 16 | 17 | val vecWire1 = Wire(Output(vecFactory)) 18 | val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) 19 | val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) 20 | 21 | vecWire1 := vecLit1 22 | vecWire1 := vecLit2 23 | printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt, vecWire1(1).asUInt) 24 | chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) 25 | chisel3.assert(vecWire1(1) === (3.25).F(8.W, 4.BP)) 26 | stop() 27 | } 28 | } 29 | } 30 | 31 | "partially initialized Vec literals should assign" in { 32 | assertTesterPasses { 33 | new BasicTester { 34 | def vecFactory = Vec(2, FixedPoint(8.W, 4.BP)) 35 | 36 | val vecWire1 = Wire(Output(vecFactory)) 37 | val vecWire2 = Wire(Output(vecFactory)) 38 | val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP)) 39 | val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP)) 40 | 41 | vecWire1 := vecLit1 42 | vecWire2 := vecLit2 43 | vecWire1(1) := (0.5).F(8.W, 4.BP) 44 | printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt, vecWire1(1).asUInt) 45 | chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP)) 46 | chisel3.assert(vecWire1(1) === (0.5).F(8.W, 4.BP)) // Last connect won 47 | chisel3.assert(vecWire2(1) === (3.25).F(8.W, 4.BP)) 48 | stop() 49 | } 50 | } 51 | } 52 | } 53 | --------------------------------------------------------------------------------