├── .github ├── CODEOWNERS ├── configs │ └── mergify_config.yml └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── .install_verilator.sh ├── .mergify.yml ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── doc ├── Algebra Types.md ├── Binary Types.md ├── Blackbox Compatibility.md ├── Convertible Types.md ├── DspContext.md ├── Example.md ├── Number Types.md ├── Numbers.md └── Testing.md ├── project ├── build.properties └── plugins.sbt ├── rocket └── src │ └── main │ └── scala │ └── jtag2mm │ └── TestMultiplexer.scala └── src ├── main ├── resources │ ├── BBFACos.v │ ├── BBFACosh.v │ ├── BBFASin.v │ ├── BBFASinh.v │ ├── BBFATan.v │ ├── BBFATan2.v │ ├── BBFATanh.v │ ├── BBFAdd.v │ ├── BBFCeil.v │ ├── BBFCos.v │ ├── BBFCosh.v │ ├── BBFDivide.v │ ├── BBFEquals.v │ ├── BBFExp.v │ ├── BBFFloor.v │ ├── BBFFromInt.v │ ├── BBFGreaterThan.v │ ├── BBFGreaterThanEquals.v │ ├── BBFHypot.v │ ├── BBFLessThan.v │ ├── BBFLessThanEquals.v │ ├── BBFLn.v │ ├── BBFLog10.v │ ├── BBFMultiply.v │ ├── BBFNotEquals.v │ ├── BBFPow.v │ ├── BBFSin.v │ ├── BBFSinh.v │ ├── BBFSqrt.v │ ├── BBFSubtract.v │ ├── BBFTan.v │ ├── BBFTanh.v │ └── BBFToInt.v ├── scala-2.12 │ └── scala │ │ └── collection.parallel │ │ └── CollectionConverters │ │ └── package.scala └── scala │ ├── dsptools │ ├── TODO │ │ ├── future_functionality │ │ │ └── BaseN_old │ │ └── move_to_utilities │ │ │ └── counters │ │ │ ├── CounterWithReset.scala │ │ │ ├── Counters_old │ │ │ └── ShiftRegisterWithReset.scala │ ├── dspmath │ │ ├── ExtendedEuclid.scala │ │ └── Factorization.scala │ ├── misc │ │ ├── BitWidth.scala │ │ ├── DspException.scala │ │ ├── DspTesterUtilities.scala │ │ └── PeekPokeDspExtensions.scala │ └── numbers │ │ ├── DspContext.scala │ │ ├── algebra_types │ │ ├── Eq.scala │ │ ├── Order.scala │ │ ├── PartialOrder.scala │ │ ├── Ring.scala │ │ ├── Signed.scala │ │ └── helpers │ │ │ ├── Comparison.scala │ │ │ └── Sign.scala │ │ ├── binary_types │ │ ├── BinaryRepresentation.scala │ │ └── NumberBits.scala │ │ ├── blackbox_compatibility │ │ ├── DspRealVerilatorBB.scala │ │ └── TrigUtility.scala │ │ ├── chisel_concrete │ │ ├── DspComplex.scala │ │ ├── DspReal.scala │ │ └── RealTrig.scala │ │ ├── chisel_types │ │ ├── DspComplexTypeClass.scala │ │ ├── DspRealTypeClass.scala │ │ ├── FixedPointTypeClass.scala │ │ ├── SIntTypeClass.scala │ │ └── UIntTypeClass.scala │ │ ├── convertible_types │ │ ├── ChiselConvertableFrom.scala │ │ └── ConvertableTo.scala │ │ ├── implicits │ │ ├── AllOps.scala │ │ ├── ImplicitSyntax.scala │ │ ├── ImplicitsTop.scala │ │ └── package.scala │ │ ├── number_types │ │ └── Numbers.scala │ │ ├── package.scala │ │ └── representations │ │ └── BaseN.scala │ └── examples │ ├── StreamingAutocorrelator.scala │ ├── TransposedStreamingFIR.scala │ └── gainOffCorr.scala └── test └── scala ├── dsptools ├── DspContextSpec.scala ├── DspTesterUtilitiesSpec.scala ├── ShiftRegisterDelaySpec.scala └── numbers │ ├── AbsSpec.scala │ ├── BaseNSpec.scala │ ├── BlackBoxFloatSpec.scala │ ├── FixedPointSpec.scala │ ├── FixedPrecisionChangerSpec.scala │ ├── LnSpec.scala │ ├── NumbersSpec.scala │ ├── ParameterizedOpSpec.scala │ └── TypeclassSpec.scala └── examples ├── DspComplexSpec.scala ├── ParameterizedAdderSpec.scala ├── ParameterizedSaturatingAdderSpec.scala ├── RealAdderSpec.scala ├── SimpleAdderSpec.scala ├── SimpleCaseClassBundleSpec.scala ├── SimpleComplexMultiplierSpec.scala ├── SimpleDspModuleSpec.scala ├── StreamingAutocorrelatorSpec.scala └── TransposedStreamFIRSpec.scala /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @freechipsproject/chisel-testers-reviewers 2 | -------------------------------------------------------------------------------- /.github/configs/mergify_config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for generating .mergify.yml 2 | conditions: 3 | - status-success=all tests passed 4 | branches: 5 | - 1.3.x 6 | - 1.4.x 7 | - 1.5.x 8 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - 1.6.x 9 | - 1.5.x 10 | - 1.4.x 11 | - 1.3.x 12 | 13 | jobs: 14 | test: 15 | name: test 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | submodules: 'true' 23 | - name: Install Verilator 24 | run: sudo apt-get update -y && sudo apt-get install -y verilator 25 | - name: Setup Scala 26 | uses: coursier/setup-action@v1 27 | - name: Cache 28 | uses: coursier/cache-action@v6 29 | - name: Formatting check 30 | id: scalafmt 31 | run: sbt scalafmtCheckAll 32 | - name: Documentation 33 | id: doc 34 | run: sbt doc 35 | - name: Test 36 | id: test 37 | run: sbt +test 38 | # Publishing steps 39 | # These steps are here to avoid duplicated work and logic 40 | - name: Setup GPG (for Publish) 41 | id: publish_start 42 | # on.push.branches above enforces we only publish from correct branches 43 | if: github.event_name == 'push' 44 | uses: olafurpg/setup-gpg@v3 45 | - name: Publish 46 | # publish_start if guards this step 47 | if: steps.publish_start.outcome != 'skipped' 48 | run: sbt ci-release 49 | env: 50 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 51 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 52 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 53 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 54 | 55 | # Sentinel job to simplify how we specify which checks need to pass in branch 56 | # protection and in Mergify 57 | # 58 | # When adding new jobs, please add them to `needs` below 59 | all_tests_passed: 60 | name: "all tests passed" 61 | needs: [test] 62 | runs-on: ubuntu-latest 63 | steps: 64 | - run: echo Success! 65 | 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### local stuff 2 | src/main/scala/dsptools/sandbox.sc 3 | test_run_dir/ 4 | *.fir 5 | *.anno 6 | *.cmd 7 | ### XilinxISE template 8 | # intermediate build files 9 | *.bgn 10 | *.bit 11 | *.bld 12 | *.cmd_log 13 | *.drc 14 | *.ll 15 | *.lso 16 | *.msd 17 | *.msk 18 | *.ncd 19 | *.ngc 20 | *.ngd 21 | *.ngr 22 | *.pad 23 | *.par 24 | *.pcf 25 | *.prj 26 | *.ptwx 27 | *.rbb 28 | *.rbd 29 | *.stx 30 | *.syr 31 | *.twr 32 | *.twx 33 | *.unroutes 34 | *.ut 35 | *.xpi 36 | *.xst 37 | *_bitgen.xwbt 38 | *_envsettings.html 39 | *_map.map 40 | *_map.mrp 41 | *_map.ngm 42 | *_map.xrpt 43 | *_ngdbuild.xrpt 44 | *_pad.csv 45 | *_pad.txt 46 | *_par.xrpt 47 | *_summary.html 48 | *_summary.xml 49 | *_usage.xml 50 | *_xst.xrpt 51 | 52 | # project-wide generated files 53 | *.gise 54 | par_usage_statistics.html 55 | usage_statistics_webtalk.html 56 | webtalk.log 57 | webtalk_pn.xml 58 | 59 | # generated folders 60 | iseconfig/ 61 | xlnx_auto_0_xdb/ 62 | xst/ 63 | _ngo/ 64 | _xmsgs/ 65 | ### Eclipse template 66 | *.pydevproject 67 | .metadata 68 | .gradle 69 | bin/ 70 | tmp/ 71 | *.tmp 72 | *.bak 73 | *.swp 74 | *~.nib 75 | local.properties 76 | .settings/ 77 | .loadpath 78 | 79 | # Eclipse Core 80 | .project 81 | 82 | # External tool builders 83 | .externalToolBuilders/ 84 | 85 | # Locally stored "Eclipse launch configurations" 86 | *.launch 87 | 88 | # CDT-specific 89 | .cproject 90 | 91 | # JDT-specific (Eclipse Java Development Tools) 92 | .classpath 93 | 94 | # Java annotation processor (APT) 95 | .factorypath 96 | 97 | # PDT-specific 98 | .buildpath 99 | 100 | # sbteclipse plugin 101 | .target 102 | 103 | # TeXlipse plugin 104 | .texlipse 105 | ### C template 106 | # Object files 107 | *.o 108 | *.ko 109 | *.obj 110 | *.elf 111 | 112 | # Precompiled Headers 113 | *.gch 114 | *.pch 115 | 116 | # Libraries 117 | *.lib 118 | *.a 119 | *.la 120 | *.lo 121 | 122 | # Shared objects (inc. Windows DLLs) 123 | *.dll 124 | *.so 125 | *.so.* 126 | *.dylib 127 | 128 | # Executables 129 | *.exe 130 | *.out 131 | *.app 132 | *.i*86 133 | *.x86_64 134 | *.hex 135 | 136 | # Debug files 137 | *.dSYM/ 138 | ### SBT template 139 | # Simple Build Tool 140 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 141 | 142 | target/ 143 | lib_managed/ 144 | src_managed/ 145 | project/boot/ 146 | .history 147 | .cache 148 | ### Emacs template 149 | # -*- mode: gitignore; -*- 150 | *~ 151 | \#*\# 152 | /.emacs.desktop 153 | /.emacs.desktop.lock 154 | *.elc 155 | auto-save-list 156 | tramp 157 | .\#* 158 | 159 | # Org-mode 160 | .org-id-locations 161 | *_archive 162 | 163 | # flymake-mode 164 | *_flymake.* 165 | 166 | # eshell files 167 | /eshell/history 168 | /eshell/lastdir 169 | 170 | # elpa packages 171 | /elpa/ 172 | 173 | # reftex files 174 | *.rel 175 | 176 | # AUCTeX auto folder 177 | /auto/ 178 | 179 | # cask packages 180 | .cask/ 181 | ### Vim template 182 | [._]*.s[a-w][a-z] 183 | [._]s[a-w][a-z] 184 | *.un~ 185 | Session.vim 186 | .netrwhist 187 | *~ 188 | ### JetBrains template 189 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 190 | 191 | *.iml 192 | 193 | ## Directory-based project format: 194 | .idea/ 195 | # if you remove the above rule, at least ignore the following: 196 | 197 | # User-specific stuff: 198 | # .idea/workspace.xml 199 | # .idea/tasks.xml 200 | # .idea/dictionaries 201 | 202 | # Sensitive or high-churn files: 203 | # .idea/dataSources.ids 204 | # .idea/dataSources.xml 205 | # .idea/sqlDataSources.xml 206 | # .idea/dynamic.xml 207 | # .idea/uiDesigner.xml 208 | 209 | # Gradle: 210 | # .idea/gradle.xml 211 | # .idea/libraries 212 | 213 | # Mongo Explorer plugin: 214 | # .idea/mongoSettings.xml 215 | 216 | ## File-based project format: 217 | *.ipr 218 | *.iws 219 | 220 | ## Plugin-specific files: 221 | 222 | # IntelliJ 223 | /out/ 224 | 225 | # mpeltonen/sbt-idea plugin 226 | .idea_modules/ 227 | 228 | # JIRA plugin 229 | atlassian-ide-plugin.xml 230 | 231 | # Crashlytics plugin (for Android Studio and IntelliJ) 232 | com_crashlytics_export_strings.xml 233 | crashlytics.properties 234 | crashlytics-build.properties 235 | ### C++ template 236 | # Compiled Object files 237 | *.slo 238 | *.lo 239 | *.o 240 | *.obj 241 | 242 | # Precompiled Headers 243 | *.gch 244 | *.pch 245 | 246 | # Compiled Dynamic libraries 247 | *.so 248 | *.dylib 249 | *.dll 250 | 251 | # Fortran module files 252 | *.mod 253 | 254 | # Compiled Static libraries 255 | *.lai 256 | *.la 257 | *.a 258 | *.lib 259 | 260 | # Executables 261 | *.exe 262 | *.out 263 | *.app 264 | ### OSX template 265 | .DS_Store 266 | .AppleDouble 267 | .LSOverride 268 | 269 | # Icon must end with two \r 270 | Icon 271 | 272 | # Thumbnails 273 | ._* 274 | 275 | # Files that might appear in the root of a volume 276 | .DocumentRevisions-V100 277 | .fseventsd 278 | .Spotlight-V100 279 | .TemporaryItems 280 | .Trashes 281 | .VolumeIcon.icns 282 | 283 | # Directories potentially created on remote AFP share 284 | .AppleDB 285 | .AppleDesktop 286 | Network Trash Folder 287 | Temporary Items 288 | .apdisk 289 | ### Xcode template 290 | # Xcode 291 | # 292 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 293 | 294 | ## Build generated 295 | build/ 296 | DerivedData 297 | 298 | ## Various settings 299 | *.pbxuser 300 | !default.pbxuser 301 | *.mode1v3 302 | !default.mode1v3 303 | *.mode2v3 304 | !default.mode2v3 305 | *.perspectivev3 306 | !default.perspectivev3 307 | xcuserdata 308 | 309 | ## Other 310 | *.xccheckout 311 | *.moved-aside 312 | *.xcuserstate 313 | ### Scala template 314 | *.class 315 | *.log 316 | 317 | # sbt specific 318 | .cache 319 | .history 320 | .lib/ 321 | dist/* 322 | target/ 323 | lib_managed/ 324 | src_managed/ 325 | project/boot/ 326 | project/plugins/project/ 327 | 328 | # Scala-IDE specific 329 | .scala_dependencies 330 | .worksheet 331 | ### Java template 332 | *.class 333 | 334 | # Mobile Tools for Java (J2ME) 335 | .mtj.tmp/ 336 | 337 | # Package Files # 338 | *.jar 339 | *.war 340 | *.ear 341 | 342 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 343 | hs_err_pid* 344 | 345 | # ignore lib from rocket build 346 | lib 347 | 348 | # bsp 349 | .bsp/ 350 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "fixedpoint"] 2 | path = fixedpoint 3 | url = https://github.com/ucb-bar/fixedpoint.git 4 | -------------------------------------------------------------------------------- /.install_verilator.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | # Install Verilator (http://www.veripool.org/projects/verilator/wiki/Installing) 3 | if [ ! -f $INSTALL_DIR/bin/verilator ]; then 4 | mkdir -p $INSTALL_DIR 5 | git clone http://git.veripool.org/git/verilator 6 | unset VERILATOR_ROOT 7 | cd verilator 8 | git pull 9 | git checkout v3.922 10 | autoconf 11 | ./configure --prefix=$INSTALL_DIR 12 | make 13 | make install 14 | export VERILATOR_ROOT=$INSTALL_DIR 15 | # Fix verilator for local install (http://www.lowrisc.org/docs/untether-v0.2/verilator/) 16 | ln -s $VERILATOR_ROOT/share/verilator/include $VERILATOR_ROOT/include 17 | ln -s $VERILATOR_ROOT/share/verilator/bin/verilator_includer $VERILATOR_ROOT/bin/verilator_includer 18 | fi 19 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | queue_rules: 2 | - name: default 3 | conditions: 4 | - status-success=all tests passed 5 | pull_request_rules: 6 | - name: automatic squash-and-merge on CI success and review 7 | conditions: 8 | - status-success=all tests passed 9 | - '#approved-reviews-by>=1' 10 | - '#changes-requested-reviews-by=0' 11 | - base=master 12 | - label="Please Merge" 13 | - label!="DO NOT MERGE" 14 | - label!="bp-conflict" 15 | actions: 16 | queue: 17 | name: default 18 | method: squash 19 | update_method: merge 20 | - name: backport to 1.5.x 21 | conditions: 22 | - merged 23 | - base=master 24 | - milestone=1.5.x 25 | actions: 26 | backport: 27 | branches: 28 | - 1.5.x 29 | labels: 30 | - Backport 31 | ignore_conflicts: true 32 | label_conflicts: bp-conflict 33 | label: 34 | add: 35 | - Backported 36 | - name: backport to 1.4.x, 1.5.x 37 | conditions: 38 | - merged 39 | - base=master 40 | - milestone=1.4.x 41 | actions: 42 | backport: 43 | branches: 44 | - 1.4.x 45 | - 1.5.x 46 | labels: 47 | - Backport 48 | ignore_conflicts: true 49 | label_conflicts: bp-conflict 50 | label: 51 | add: 52 | - Backported 53 | - name: backport to 1.3.x, 1.4.x, 1.5.x 54 | conditions: 55 | - merged 56 | - base=master 57 | - milestone=1.3.x 58 | actions: 59 | backport: 60 | branches: 61 | - 1.3.x 62 | - 1.4.x 63 | - 1.5.x 64 | labels: 65 | - Backport 66 | ignore_conflicts: true 67 | label_conflicts: bp-conflict 68 | label: 69 | add: 70 | - Backported 71 | - name: automatic squash-and-mege of 1.3.x backport PRs 72 | conditions: 73 | - status-success=all tests passed 74 | - '#changes-requested-reviews-by=0' 75 | - base=1.3.x 76 | - label="Backport" 77 | - label!="DO NOT MERGE" 78 | - label!="bp-conflict" 79 | actions: 80 | queue: 81 | name: default 82 | method: squash 83 | update_method: merge 84 | - name: automatic squash-and-mege of 1.4.x backport PRs 85 | conditions: 86 | - status-success=all tests passed 87 | - '#changes-requested-reviews-by=0' 88 | - base=1.4.x 89 | - label="Backport" 90 | - label!="DO NOT MERGE" 91 | - label!="bp-conflict" 92 | actions: 93 | queue: 94 | name: default 95 | method: squash 96 | update_method: merge 97 | - name: automatic squash-and-mege of 1.5.x backport PRs 98 | conditions: 99 | - status-success=all tests passed 100 | - '#changes-requested-reviews-by=0' 101 | - base=1.5.x 102 | - label="Backport" 103 | - label!="DO NOT MERGE" 104 | - label!="bp-conflict" 105 | actions: 106 | queue: 107 | name: default 108 | method: squash 109 | update_method: merge 110 | 111 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | enablePlugins(SiteScaladocPlugin) 4 | 5 | enablePlugins(GhpagesPlugin) 6 | 7 | val defaultVersions = Map( 8 | "chisel" -> "5.1.0", 9 | "chiseltest" -> "5.0.2" 10 | ) 11 | 12 | name := "dsptools" 13 | 14 | val commonSettings = Seq( 15 | scalaVersion := "2.13.10", 16 | scalacOptions ++= Seq("-encoding", 17 | "UTF-8", 18 | "-unchecked", 19 | "-deprecation", 20 | "-feature", 21 | "-language:reflectiveCalls", 22 | "-Xfatal-warnings", 23 | "-Ymacro-annotations"), 24 | resolvers ++= Resolver.sonatypeOssRepos("snapshots"), 25 | resolvers ++= Resolver.sonatypeOssRepos("releases"), 26 | libraryDependencies ++= { 27 | CrossVersion.partialVersion(scalaVersion.value) match { 28 | case Some((2, major)) if major <= 12 => Seq() 29 | case _ => Seq("org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4") 30 | } 31 | }, 32 | libraryDependencies ++= Seq("chisel").map { dep: String => 33 | "org.chipsalliance" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) 34 | }, 35 | addCompilerPlugin(("org.chipsalliance" %% "chisel-plugin" % defaultVersions("chisel")).cross(CrossVersion.full)), 36 | ) 37 | 38 | val dsptoolsSettings = Seq( 39 | name := "dsptools", 40 | organization := "edu.berkeley.cs", 41 | version := "1.6-SNAPSHOT", 42 | git.remoteRepo := "git@github.com:ucb-bar/dsptools.git", 43 | autoAPIMappings := true, 44 | libraryDependencies ++= Seq( 45 | "org.typelevel" %% "spire" % "0.18.0", 46 | "org.scalanlp" %% "breeze" % "2.1.0", 47 | "org.scalatest" %% "scalatest" % "3.2.15" % "test" 48 | ), 49 | libraryDependencies ++= Seq("chiseltest").map { dep: String => 50 | "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) 51 | }, 52 | pomExtra := http://chisel.eecs.berkeley.edu/ 53 | 54 | 55 | apache_v2 56 | https://opensource.org/licenses/Apache-2.0 57 | repo 58 | 59 | 60 | 61 | 62 | grebe 63 | Paul Rigge 64 | http://www.eecs.berkeley.edu/~rigge/ 65 | 66 | 67 | shunshou 68 | Angie Wang 69 | https://www.linkedin.com/in/angie-wang-ee/ 70 | 71 | 72 | chick 73 | Charles Markley 74 | https://aspire.eecs.berkeley.edu/author/chick/ 75 | 76 | , 77 | publishTo := { 78 | val v = version.value 79 | val nexus = "https://oss.sonatype.org/" 80 | if (v.trim.endsWith("SNAPSHOT")) { 81 | Some("snapshots".at(nexus + "content/repositories/snapshots")) 82 | } else { 83 | Some("releases".at(nexus + "service/local/staging/deploy/maven2")) 84 | } 85 | }, 86 | ) 87 | 88 | val fixedpointSettings = Seq( 89 | libraryDependencies ++= Seq( 90 | "org.scalatest" %% "scalatest" % "3.2.15" % "test", 91 | "org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0" % "test", 92 | ) 93 | ) 94 | 95 | publishMavenStyle := true 96 | 97 | Test / publishArtifact := false 98 | pomIncludeRepository := { x => 99 | false 100 | } 101 | 102 | def freshProject(name: String, dir: File): Project = { 103 | Project(id = name, base = dir / "src") 104 | .settings( 105 | Compile / scalaSource := baseDirectory.value / "main" / "scala", 106 | Compile / resourceDirectory := baseDirectory.value / "main" / "resources" 107 | ) 108 | } 109 | 110 | lazy val fixedpoint = freshProject("fixedpoint", file("fixedpoint")) 111 | .settings( 112 | commonSettings, 113 | fixedpointSettings 114 | ) 115 | 116 | val dsptools = (project in file(".")) 117 | .dependsOn(fixedpoint) 118 | //.enablePlugins(BuildInfoPlugin) 119 | .enablePlugins(ScalaUnidocPlugin) 120 | .settings(commonSettings) 121 | .settings(dsptoolsSettings) 122 | -------------------------------------------------------------------------------- /doc/Algebra Types.md: -------------------------------------------------------------------------------- 1 | Abstract Algebra Type Classes ([Spire](https://github.com/non/spire)) 2 | 3 | Listed here are hierarchically presented type classes and operations associated with them that return Chisel base types (Bool or numbers). 4 | 5 | # `Ring` (based on `Ring` from Spire) 6 | * plus (+) 7 | * minus (-) 8 | * negate (unary_-) 9 | * times (*) 10 | * one 11 | * zero 12 | * context_+ 13 | * context_- (binary and unary) 14 | * context_* 15 | 16 | > Note: `context_+`, `context_-`, and `context_*` all have associated pipelining amounts [+, -, negate associated with `numAddPipes`; * associated with `numMulPipes` set in `DspContext`]. You can control the overflow behavior of `context_+` and `context_-` via `overflowType` in `DspContext`. Finally, for *, you can control binary trim behavior for `FixedPoint` #'s via `trimType` and `binaryPointGrowth` in `DspContext`. 17 | 18 | # `Eq` 19 | * eqv (===) 20 | * neqv (=!=) 21 | 22 | # `PartialOrder extends Eq ` 23 | * *Not likely to be practically useful for many Chisel designs, but it is part of the hierarchy in Spire* 24 | * eqv (===) 25 | * lteqv (<=) 26 | * lt (<) 27 | * gteqv (>=) 28 | * gt (>) 29 | 30 | # `Order extends PartialOrder` 31 | * eqv (===) 32 | * gt (>) 33 | * lt (<) 34 | * gteqv (>=) 35 | * lteqv (<=) 36 | * min 37 | * max 38 | 39 | # `Signed` 40 | * abs 41 | * isSignZero 42 | * isSignPositive 43 | * isSignNegative 44 | * isSignNonZero 45 | * isSignNonPositive 46 | * isSignNonNegative 47 | -------------------------------------------------------------------------------- /doc/Binary Types.md: -------------------------------------------------------------------------------- 1 | * *RealBits* extends *Real* with *BinaryRepresentation* and *ChiselConvertableFrom* 2 | * *IntegerBits* extends *RealBits* with *Integer* 3 | 4 | These form the underlying types of all Chisel numeric type classes. 5 | 6 | # BinaryRepresentation 7 | Typeclasses like `Real` or `Integer` are useful because they abstract away the details of how the underlying numbers are represented, allowing abstract generators to be reused. 8 | Sometimes, knowledge of the underlying representation of a number can be used to implement some operations more efficiently. 9 | A well known example for binary representations of numbers is that multiplying or dividing by a power of two can be implemented efficiently with shifts. 10 | 11 | The `BinaryRepresentation` typeclass adds functions for implementing multiplication and division (and some other small utilities) by powers of two using shifts: 12 | * `shl` (Int or UInt amount) 13 | * `shr` (Int or UInt amount) -- Rounds to negative infinity i.e. for negative #'s, continual >> will never be smaller in magnitude than -LSB 14 | * `signBit` 15 | * `div2` (More proper division than shr i.e. rounds [choice via DSP Context] for SInt and adds more binary points for FixedPoint [Can be trimmed via context]) 16 | * `mul2` (identical to shl by an Int amount) 17 | * `trimBinary` (trim fractional bits with rounding mode selected via DspContext; doesn't affect DspReal) 18 | 19 | -------------------------------------------------------------------------------- /doc/Blackbox Compatibility.md: -------------------------------------------------------------------------------- 1 | # Blackbox Compatibility 2 | A number of operations for `DspReal`s have been implemented via Chisel blackboxes. 3 | Some operations have equivalents that are synthesizeable- these are useful for testing `FixedPoint` designs with better finite precision effects. 4 | Some operations do not have synthesizeable equivalents- these are useful for modeling (e.g. an ADC). 5 | 6 | ## Operations that are equivalents for synthesizeable types (e.g. FixedPoint) 7 | * Add 8 | * Subtract 9 | * Multiply 10 | * Divide (Chisel only deals with division by powers of 2) 11 | * > 12 | * >= 13 | * < 14 | * <= 15 | * == 16 | * != 17 | * Floor 18 | * Ceil 19 | 20 | ## Conversion functions between synthesizable Chisel number types + DspReal (non-synthesizeable, use with caution!) 21 | 22 | > Note: These are only good for golden model testing. They should *never* be used in your final design. 23 | 24 | * SInt -> Real 25 | * Real -> SInt 26 | 27 | ## Non-synthesizable operations that don't have Chisel number type equivalents (with Verilator support) 28 | 29 | > Note: These work with Verilator or Treadle, but can't be used with type classes. 30 | 31 | * Ln 32 | * Log10 33 | * Exp 34 | * Sqrt 35 | * Pow 36 | 37 | ## Non-synthesizable operations (no Verilator support) 38 | 39 | > These don't have Chisel number type equivalents and work with FIRRTL interpreter. Verilator doesn't support these :( but we built out approximation functions with no guarantees on precision...) 40 | 41 | * Sin 42 | * Cos 43 | * Tan 44 | * ASin 45 | * ACos 46 | * ATan 47 | * ATan2 48 | * Hypot 49 | * Sinh 50 | * Cosh 51 | * Tanh 52 | * ASinh 53 | * ACosh 54 | * ATanh 55 | -------------------------------------------------------------------------------- /doc/Convertible Types.md: -------------------------------------------------------------------------------- 1 | # `ConvertableTo` 2 | * fromDouble (to type; gets binary point from type -- if needed) 3 | * fromDoubleWithFixedWidth (to type; gets binary point *and* width from type -- if needed) 4 | 5 | # `ChiselConvertableFrom` (Specific to Chisel base number types! I.e. UInt, SInt, FixedPoint, DspReal) 6 | * intPart (takes the integer portion without rounding and converts it to an SInt) 7 | * asFixed (provide the prototype to get a binary point location) 8 | * asReal 9 | 10 | > **WARNING**: Binary points and widths *should* be known if these methods are going to be used! 11 | -------------------------------------------------------------------------------- /doc/DspContext.md: -------------------------------------------------------------------------------- 1 | BIG WARNING: If you want to directly use UInt, SInt, & FixedPoint without passing them through some generic, you **should not** use the Ring operators +, -, *, unary_- *if* you want to use DspContext. Using + on a normal UInt will result in Chisel + behavior (wrapped addition). To guarantee that the Ring operators follow DspContext, after importing implicits, you should instead use: 2 | * a context_+ b 3 | * a context_- b 4 | * a context_* b 5 | * a.context_unary_- 6 | 7 | We need to come up with better names, but at least this makes it easy to search for context_... 8 | 9 | # DspContext 10 | DspContext allows you to control how some operations are performed within a scope. 11 | ``` 12 | DspContext.alter(newContext) { 13 | // new scope with settings from newContext 14 | } 15 | ``` 16 | There are a number of helpers that can be used to set one field of the context, e.g. `DspContext.withTrimType`. 17 | 18 | The `DspContext` case class contains the following fields: 19 | * `overflowType` (type: `OverflowType`; default: `Grow`) specifies overflow behavior for ops like a + b, a - b, -a 20 | * `Saturate` (not implemented) 21 | * `Wrap`: Wrap output on overflow (output has max width of either input) 22 | * `Grow`: Add MSBs so your output is numerically correct 23 | * `trimType` (type: TrimType; default: Floor) specifies how Fixed point ops like a * b, a.trimBinary(n), and a.div2(n) should round results 24 | * `NoTrim`: Keep maximal precision + bit growth 25 | * `Truncate` (not implemented) 26 | * `RoundHalfUp`: Assumes binaryPoints are well defined, 27 | * For `times` and `div2`: Rounds half up to a.binaryPoint.get.max(b.binaryPoint.get) + DspContext.binaryPointGrowth # of fractional bits -- looks at the result's next bit 28 | * For trimBinary: Rounds half up to n fractional bits 29 | * **WARNING**: The overflow behavior when you try to round up the largest representable positive FixedPoint value is defined by DspContext.overflowType. It's only guaranteed to be mathematically correct if you grow! 30 | * `Floor`: Rounds towards negative infinity; # of fractional bits is the same as in RoundHalfUp case 31 | * Caution: Any time a known binary point is expected, you might run into Chisel/Firrtl crashes if an unknown binary point creeps in. Please let us know if you are running into issues with library code. 32 | * `binaryPointGrowth` (type: Int; default: 1) 33 | * Use case explained above 34 | * Requires that the input binary point is well defined 35 | * `binaryPoint` (type: `Option[Int]`; default: `Some(14)`) 36 | * Specifies the default # of fractional bits when creating `FixedPoint` literals with something like `ConvertableTo[T].fromDouble(3.14)` 37 | * `numBits` (type: `Option[Int]`; default: `Some(16)`, unused) 38 | * `complexUse4Muls` (type: Boolean, default: false) 39 | * true: Use 4 real multiplies to perform a complex multiply 40 | * false: Use 3 real multiplies to perform a complex multiply 41 | * `numMulPipes` (type: `Int`; default: 0) 42 | * # of pipeline registers to add after a multiply operation between two inputs of type `[T <: Data:RealBits]` 43 | * Note: This only applies to multiplications with `[T <: Data:RealBits]`. `DspComplex` multiplication utilizes some combination of this and `numAddPipes` 44 | * `numAddPipes` (type: `Int`; default: 0) 45 | * # of pipeline registers to add after an add operation between two inputs of type `[T <: Data:RealBits]` 46 | 47 | ## How to Use 48 | * You must `import dsptools.numbers._` 49 | * You can change the DspContext @ any level (Top, Module, local operations) based off of where you wrap the change i.e. what you surround by `DspContext.with...{ ... }` 50 | * **Example:** Changing the local +, - overflow behavior (while keeping other options the same; only for the operations inside the braces -- otherwise use defaults) 51 | * `val sum = DspContext.withOverflowType(Wrap) { a context_+ b }` 52 | * `val prod = DspContext.withTrimType(RoundHalfUp) { a context_* b }` 53 | * `val prod = DspContext.withBinaryPointGrowth(3) { a context_* b }` 54 | * `val lit = DspContext.withBinaryPoint(8) { ConvertableTo[FixedPoint].fromDouble(3.14) }` 55 | * `val prod = DspContext.withComplexUse4Muls(true) { ca context_* cb }` 56 | * `val prod = DspContext.withNumMulPipes(2) { a context_* b }` 57 | * `val sum = DspContext.withNumAddPipes(1) { a context_+ b }` 58 | * **Example:** Change several options locally: 59 | ``` 60 | val prod = DspContext.alter(DspContext.current.copy(trimType = NoTrim, binaryPointGrowth = 3, numMulPipes = 2)) { a * b } 61 | ``` 62 | * Get the number pipeline registers used in a Complex multiply: 63 | * `val delay = DspContext.current.complexMulDly` 64 | 65 | -------------------------------------------------------------------------------- /doc/Example.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | A basic DSP Module + Tester might look like this: 4 | 5 | ```scala 6 | package SimpleDsp 7 | 8 | // Allows you to use Chisel Module, Bundle, etc. 9 | import chisel3._ 10 | // Allows you to use FixedPoint 11 | import fixedpoint._ 12 | // If you want to take advantage of type classes >> Data:RealBits (i.e. pass in FixedPoint or DspReal) 13 | // Required for you to use operators defined via type classes (+ has special Dsp overflow behavior, etc.) 14 | import dsptools.numbers._ 15 | // Enables you to set DspContext's for things like overflow behavior, rounding modes, etc. 16 | import dsptools.DspContext 17 | // Use DspTester, specify options for testing (i.e. expect tolerances on fixed point, etc.) 18 | import dsptools.{DspTester, DspTesterOptionsManager, DspTesterOptions} 19 | // Allows you to modify default Chisel tester behavior (note that DspTester is a special version of Chisel tester) 20 | import iotesters.TesterOptions 21 | // Scala unit testing style 22 | import org.scalatest.{FlatSpec, Matchers} 23 | 24 | // IO Bundle. Note that when you parameterize the bundle, you may need to override cloneType. 25 | // This also creates x, y, z inputs/outputs (direction must be specified at some IO hierarchy level) 26 | // of the type you specify via gen (must be Data:RealBits = UInt, SInt, FixedPoint, DspReal) 27 | class SimpleDspIo[T <: Data:RealBits](gen: T) extends Bundle { 28 | val x = Input(gen) 29 | val y = Input(gen) 30 | val z = Output(gen) 31 | override def cloneType: this.type = new SimpleDspIo(gen).asInstanceOf[this.type] 32 | } 33 | 34 | // Parameterized Chisel Module; takes in type parameters as explained above 35 | class SimpleDspModule[T <: Data:RealBits](gen: T, val addPipes: Int) extends Module { 36 | // This is how you declare an IO with parameters 37 | val io = IO(new SimpleDspIo(gen)) 38 | // Output will be current x + y addPipes clock cycles later 39 | // Note that this relies on the fact that type classes have a special + that 40 | // add addPipes # of ShiftRegister after the sum. If you don't wrap the sum in 41 | // DspContext.withNumAddPipes(addPipes), the default # of addPipes is used. 42 | DspContext.withNumAddPipes(addPipes) { 43 | io.z := io.x + io.y 44 | } 45 | } 46 | 47 | // You create a tester that must extend DspTester to support Dsp type peeks/pokes (with doubles, complex, etc.) 48 | class SimpleDspModuleTester[T <: Data:RealBits](c: SimpleDspModule[T]) extends DspTester(c) { 49 | val x = Seq(-1.1, -0.4, 0.4, 1.1) 50 | val z = x map (2 * _) 51 | for (i <- 0 until (x.length + c.addPipes)) { 52 | val in = x(i % x.length) 53 | // Feed in to the x, y inputs 54 | poke(c.io.x, in) 55 | // Locally (just for the stuff in {}) change console print properties 56 | // so that this second peek isn't displayed on the console 57 | // (since the input value is the same as the first peek) 58 | updatableDspVerbose.withValue(false) { 59 | poke(c.io.y, in) 60 | } 61 | if (i >= c.addPipes) { 62 | // Expect that the z output matches the expected value @ z(i - c.addPipes) to some tolerance 63 | // as described below 64 | expect(c.io.z, z(i - c.addPipes)) 65 | } 66 | // Step the clock by 1 period 67 | step(1) 68 | } 69 | } 70 | 71 | // Scala style testing 72 | class SimpleDspModuleSpec extends FlatSpec with Matchers { 73 | 74 | // If you don't want to use default tester options, you need to create your own DspTesterOptionsManager 75 | val testOptions = new DspTesterOptionsManager { 76 | // Customizing Dsp-specific tester features (unspecified options remain @ default values) 77 | dspTesterOptions = DspTesterOptions( 78 | // # of bits of error tolerance allowed by expect (for FixedPoint, UInt, SInt type classes) 79 | fixTolLSBs = 1, 80 | // Generate a Verilog testbench to mimic peek/poke testing 81 | genVerilogTb = true, 82 | // Show all tester interactions with the module (not just failed expects) on the console 83 | isVerbose = true) 84 | // Customizing Chisel tester features 85 | testerOptions = TesterOptions( 86 | // If set to true, prints out all nested peeks/pokes (i.e. for FixedPoint or DspReal, would 87 | // print out BigInt or base n versions of peeks/pokes -- rather than the proper decimal representation) 88 | isVerbose = false, 89 | // Default backend uses FirrtlInterpreter. If you want to simulate with the generated Verilog, 90 | // you need to switch the backend to Verilator. Note that tests currently need to be dumped in 91 | // different output directories with Verilator; otherwise you run into weird concurrency issues (bug!)... 92 | backendName = "verilator") 93 | // Override default output directory while maintaining other default settings 94 | commonOptions = commonOptions.copy(targetDirName = "test_run_dir/simple_dsp_fix") 95 | } 96 | 97 | behavior of "simple dsp module" 98 | 99 | it should "properly add fixed point types" in { 100 | // Run the dsp tester by following this style: You need to pass in the Chisel Module [SimpleDspModule] 101 | // to test and your created DspTesterOptionsManager [testOptions]. You must also specify the tester 102 | // [SimpleDspModuleTester] to run with the module. This tester should be something that extends DspTester. 103 | // Note that here, you're testing the module with inputs/outputs of FixedPoint type (Q15.12) 104 | // and 3 registers (for retiming) at the output. You could alternatively use DspReal() 105 | // Scala keeps track of which tests pass/fail; the execute method returns true if the test passes. 106 | // Supposedly, Chisel3 testing infrastructure might be overhauled to reduce the amount of boilerplate, 107 | // but this is currently the endorsed way to do things. 108 | dsptools.Driver.execute(() => new SimpleDspModule(FixedPoint(16.W, 12.BP), addPipes = 3), testOptions) { c => 109 | new SimpleDspModuleTester(c) 110 | } should be (true) 111 | } 112 | 113 | } 114 | ``` 115 | 116 | Please read the above code + comments carefully. 117 | 118 | It shows you how to create a parameterized module + IO bundle (API might change again...) with generic type classes to allow you to test your "math" both with real numbers (that result in numerically correct outputs) and fixed point numbers (that allow you to factor in quantization, etc. and are actually synthesizable). Note the need of cloneType for parameterized bundles! 119 | 120 | It also demonstrates a simple example of changing the Dsp Context. You can do this locally (per operation), at the module level, or at the top level (simply affected by where you wrap the DspContext.withNumAddPipes(addPipes) {}). 121 | 122 | The example also shows you how a tester interacts with the DUT via peek and expect and how to change tester options like expect tolerances. You can change tester options globally via what's passed in to the DspTesterOptionsManager or you can change some of them (for example, display) locally -- just for some portions of the tester operation. 123 | 124 | To run this single test, you can use the command `sbt "testOnly SimpleDsp.SimpleDspModuleSpec"`. Note that `sbt test` runs all tests in *src/test/scala*. 125 | -------------------------------------------------------------------------------- /doc/Number Types.md: -------------------------------------------------------------------------------- 1 | In writing a generator, you will probably use typeclasses like `Real` or `Integer` that map naturally to many of the Chisel types you are familiar with. 2 | These typeclasses (implemented in Scala as `trait`s) extend a number of more granular typeclasses. 3 | 4 | Library authors may find it useful to understand these more granular typeclasses so they can write generators that work for both `Real` and `Integer` typeclasses, for example. 5 | 6 | * A number that *IsIntegral* also *IsReal*. 7 | 8 | * A *Real* number *IsReal* and can be operated on in a *Ring*. Some type conversion is supported via *ConvertableTo* (e.g. *fromDouble*). 9 | * An *Integer* is a *Real* number that also *IsIntegral*. 10 | 11 | # `IsReal` 12 | * inherits all operations from `Order` and `Signed` 13 | * `ceil` 14 | * `floor` 15 | * `truncate` (integer portion) 16 | * `round` (0.5 fractional part -> tie breaking to positive infinity i.e. round half up) 17 | * `isWhole` 18 | 19 | > See [Rounding Modes Wiki](https://en.wikipedia.org/wiki/Rounding) for properties. 20 | 21 | # `IsIntegral` 22 | * inherits all operations from `IsReal` 23 | * `mod` 24 | * `isOdd` 25 | * `isEven` 26 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.8.2 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1") 3 | 4 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2") 5 | 6 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.0") 7 | 8 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") 9 | 10 | addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.3") 11 | 12 | addSbtPlugin("com.eed3si9n" % "sbt-sriracha" % "0.1.0") 13 | 14 | addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.4") 15 | 16 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") 17 | -------------------------------------------------------------------------------- /rocket/src/main/scala/jtag2mm/TestMultiplexer.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package freechips.rocketchip.jtag2mm 4 | 5 | import chisel3._ 6 | import chisel3.util._ 7 | import chisel3.experimental.{FixedPoint => _, _} 8 | import fixedpoint._ 9 | //import chisel3.experimental.{withClockAndReset} 10 | 11 | import dsptools._ 12 | import dsptools.numbers._ 13 | 14 | import dspblocks._ 15 | import freechips.rocketchip.amba.axi4._ 16 | import freechips.rocketchip.amba.axi4stream._ 17 | import freechips.rocketchip.config._ 18 | import freechips.rocketchip.diplomacy._ 19 | import freechips.rocketchip.regmapper._ 20 | import freechips.rocketchip.tilelink._ 21 | 22 | 23 | abstract class TestMultiplexer[D, U, EO, EI, B <: Data]()(implicit p: Parameters) 24 | extends DspBlock[D, U, EO, EI, B] with HasCSR { 25 | 26 | val streamNode = AXI4StreamMasterNode(Seq(AXI4StreamMasterPortParameters(Seq(AXI4StreamMasterParameters("out", n = 8))))) 27 | 28 | lazy val module = new LazyModuleImp(this) { 29 | val (out, _) = streamNode.out.unzip 30 | 31 | val a = RegInit(UInt(64.W), 0.U) 32 | val b = RegInit(UInt(64.W), 0.U) 33 | val select = RegInit(Bool(), true.B) 34 | 35 | regmap(0x00 -> Seq(RegField(64, a)), 0x08 -> Seq(RegField(64, b)), 0x10 -> Seq(RegField(1, select))) 36 | 37 | out.head.bits.data := Mux(select, a, b) 38 | } 39 | } 40 | 41 | class TLMultiplexer()(implicit p: Parameters) 42 | extends TestMultiplexer[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]() 43 | with TLBasicBlock 44 | 45 | class Jtag2TLMultiplexer( 46 | irLength: Int, 47 | initialInstruction: BigInt, 48 | beatBytes: Int, 49 | jtagAddresses: AddressSet, 50 | maxBurstNum: Int) 51 | extends LazyModule()(Parameters.empty) { 52 | 53 | val multiplexerModule = LazyModule(new TLMultiplexer() { 54 | 55 | def standaloneParams = TLBundleParameters( 56 | addressBits = 16, 57 | dataBits = 64, 58 | sourceBits = 16, 59 | sinkBits = 16, 60 | sizeBits = 3, 61 | echoFields = Nil, 62 | requestFields = Nil, 63 | responseFields = Nil, 64 | hasBCE = false 65 | ) 66 | 67 | val clientParams = TLClientParameters( 68 | name = "BundleBridgeToTL", 69 | sourceId = IdRange(0, 1), 70 | nodePath = Seq(), 71 | requestFifo = false, 72 | visibility = Seq(AddressSet(0, ~0)), 73 | supportsProbe = TransferSizes(1, beatBytes), 74 | supportsArithmetic = TransferSizes(1, beatBytes), 75 | supportsLogical = TransferSizes(1, beatBytes), 76 | supportsGet = TransferSizes(1, beatBytes), 77 | supportsPutFull = TransferSizes(1, beatBytes), 78 | supportsPutPartial = TransferSizes(1, beatBytes), 79 | supportsHint = TransferSizes(1, beatBytes) 80 | ) 81 | 82 | val ioMem = mem.map { m => 83 | { 84 | val ioMemNode = BundleBridgeSource(() => TLBundle(standaloneParams)) 85 | m := BundleBridgeToTL(TLClientPortParameters(Seq(clientParams))) := ioMemNode 86 | val ioMem = InModuleBody { ioMemNode.makeIO() } 87 | ioMem 88 | } 89 | } 90 | 91 | val ioStreamNode = BundleBridgeSink[AXI4StreamBundle]() 92 | ioStreamNode := 93 | AXI4StreamToBundleBridge(AXI4StreamSlaveParameters()) := streamNode 94 | val outStream = InModuleBody { ioStreamNode.makeIO() } 95 | }) 96 | 97 | val jtagModule = LazyModule( 98 | new TLJTAGToMasterBlock(irLength, initialInstruction, beatBytes, jtagAddresses, maxBurstNum) 99 | ) 100 | 101 | InModuleBody { multiplexerModule.ioMem.get <> jtagModule.ioTL } 102 | 103 | def makeIO1(): AXI4StreamBundle = { 104 | val io2: AXI4StreamBundle = IO(multiplexerModule.outStream.cloneType) 105 | io2.suggestName("outStream") 106 | io2 <> multiplexerModule.outStream 107 | io2 108 | } 109 | def makeIO2(): TopModuleIO = { 110 | val io2: TopModuleIO = IO(jtagModule.ioJTAG.cloneType) 111 | io2.suggestName("ioJTAG") 112 | io2 <> jtagModule.ioJTAG 113 | io2 114 | } 115 | 116 | val outStream = InModuleBody { makeIO1() } 117 | val ioJTAG = InModuleBody { makeIO2() } 118 | 119 | lazy val module = new LazyModuleImp(this) 120 | } 121 | 122 | class AXI4Multiplexer()(implicit p: Parameters) 123 | extends TestMultiplexer[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4EdgeParameters, AXI4EdgeParameters, AXI4Bundle] 124 | with AXI4BasicBlock 125 | 126 | class Jtag2AXI4Multiplexer( 127 | irLength: Int, 128 | initialInstruction: BigInt, 129 | beatBytes: Int, 130 | jtagAddresses: AddressSet, 131 | maxBurstNum: Int) 132 | extends LazyModule()(Parameters.empty) { 133 | 134 | val multiplexerModule = LazyModule(new AXI4Multiplexer() { 135 | 136 | def standaloneParams = AXI4BundleParameters(addrBits = 8, dataBits = beatBytes*8, idBits = 1) 137 | val ioMem = mem.map { 138 | m => { 139 | val ioMemNode = BundleBridgeSource(() => AXI4Bundle(standaloneParams)) 140 | m := BundleBridgeToAXI4(AXI4MasterPortParameters(Seq(AXI4MasterParameters("bundleBridgeToAXI4")))) := ioMemNode 141 | val ioMem = InModuleBody { ioMemNode.makeIO() } 142 | ioMem 143 | } 144 | } 145 | 146 | val ioStreamNode = BundleBridgeSink[AXI4StreamBundle]() 147 | ioStreamNode := 148 | AXI4StreamToBundleBridge(AXI4StreamSlaveParameters()) := streamNode 149 | val outStream = InModuleBody { ioStreamNode.makeIO() } 150 | }) 151 | 152 | val jtagModule = LazyModule( 153 | new AXI4JTAGToMasterBlock(irLength, initialInstruction, beatBytes, jtagAddresses, maxBurstNum) 154 | ) 155 | 156 | InModuleBody { multiplexerModule.ioMem.get <> jtagModule.ioAXI4 } 157 | 158 | def makeIO1(): AXI4StreamBundle = { 159 | val io2: AXI4StreamBundle = IO(multiplexerModule.outStream.cloneType) 160 | io2.suggestName("outStream") 161 | io2 <> multiplexerModule.outStream 162 | io2 163 | } 164 | def makeIO2(): TopModuleIO = { 165 | val io2: TopModuleIO = IO(jtagModule.ioJTAG.cloneType) 166 | io2.suggestName("ioJTAG") 167 | io2 <> jtagModule.ioJTAG 168 | io2 169 | } 170 | 171 | val outStream = InModuleBody { makeIO1() } 172 | val ioJTAG = InModuleBody { makeIO2() } 173 | 174 | lazy val module = new LazyModuleImp(this) 175 | } 176 | 177 | /*object JTAGToTLMultiplexerApp extends App { 178 | 179 | val irLength = 4 180 | val initialInstruction = BigInt("0", 2) 181 | val addresses = AddressSet(0x00000, 0x3fff) 182 | val beatBytes = 8 183 | val maxBurstNum = 8 184 | 185 | implicit val p: Parameters = Parameters.empty 186 | val appModule = LazyModule( 187 | new Jtag2TLMultiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) 188 | ) 189 | 190 | chisel3.Driver.execute(args, () => appModule.module) 191 | } 192 | 193 | object JTAGToAXI4MultiplexerApp extends App { 194 | 195 | val irLength = 4 196 | val initialInstruction = BigInt("0", 2) 197 | val addresses = AddressSet(0x00000, 0x3fff) 198 | val beatBytes = 8 199 | val maxBurstNum = 8 200 | 201 | implicit val p: Parameters = Parameters.empty 202 | val appModule = LazyModule( 203 | new Jtag2AXI4Multiplexer(irLength, initialInstruction, beatBytes, addresses, maxBurstNum) 204 | ) 205 | 206 | chisel3.Driver.execute(args, () => appModule.module) 207 | }*/ 208 | -------------------------------------------------------------------------------- /src/main/resources/BBFACos.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFACos( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($acos($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFACosh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFACosh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($acosh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFASin.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFASin( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($asin($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFASinh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFASinh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($asinh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFATan.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFATan( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($atan($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFATan2.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFATan2( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($atan2($bitstoreal(in1), $bitstoreal(in2))); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFATanh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFATanh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($atanh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFAdd.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFAdd( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($bitstoreal(in1) + $bitstoreal(in2)); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFCeil.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFCeil( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($ceil($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFCos.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFCos( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($cos($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFCosh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFCosh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($cosh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFDivide.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFDivide( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($bitstoreal(in1) / $bitstoreal(in2)); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFEquals.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFEquals( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) == $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFExp.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFExp( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($exp($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFFloor.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFFloor( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($floor($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFFromInt.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFFromInt( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($itor($signed(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFGreaterThan.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFGreaterThan( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) > $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFGreaterThanEquals.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFGreaterThanEquals( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) >= $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFHypot.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFHypot( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($hypot($bitstoreal(in1), $bitstoreal(in2))); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFLessThan.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFLessThan( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) < $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFLessThanEquals.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFLessThanEquals( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) <= $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFLn.v: -------------------------------------------------------------------------------- 1 | /** Math operations from IEEE.1364-2005 **/ 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module BBFLn( 5 | input [63:0] in, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($ln($bitstoreal(in))); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFLog10.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFLog10( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($log10($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFMultiply.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFMultiply( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($bitstoreal(in1) * $bitstoreal(in2)); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFNotEquals.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFNotEquals( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg out 7 | ); 8 | always @* begin 9 | out = $bitstoreal(in1) != $bitstoreal(in2); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFPow.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFPow( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($pow($bitstoreal(in1), $bitstoreal(in2))); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFSin.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFSin( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($sin($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFSinh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFSinh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($sinh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFSqrt.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFSqrt( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($sqrt($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFSubtract.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFSubtract( 4 | input [63:0] in1, 5 | input [63:0] in2, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $realtobits($bitstoreal(in1) - $bitstoreal(in2)); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/BBFTan.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFTan( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($tan($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFTanh.v: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | module BBFTanh( 4 | input [63:0] in, 5 | output reg [63:0] out 6 | ); 7 | always @* begin 8 | out = $realtobits($tanh($bitstoreal(in))); 9 | end 10 | endmodule 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/BBFToInt.v: -------------------------------------------------------------------------------- 1 | // WARNING! May cause overflow! 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module BBFToInt( 5 | input [63:0] in, 6 | output reg [63:0] out 7 | ); 8 | always @* begin 9 | out = $rtoi($bitstoreal(in)); 10 | end 11 | endmodule 12 | 13 | -------------------------------------------------------------------------------- /src/main/scala-2.12/scala/collection.parallel/CollectionConverters/package.scala: -------------------------------------------------------------------------------- 1 | package scala.collection.parallel 2 | 3 | package object CollectionConverters { 4 | object RangeIsParallelizable { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/TODO/move_to_utilities/counters/CounterWithReset.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.counters 4 | 5 | import chisel3._ 6 | 7 | object CounterWithReset { 8 | def apply(cond: Bool, n: Int, reset: Bool): (UInt, Bool) = { 9 | val c = chisel3.util.Counter(cond, n) 10 | if (n > 1) { when(reset) { c._1 := 0.U } } 11 | c 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/TODO/move_to_utilities/counters/Counters_old: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.counters 4 | 5 | import chisel3._ 6 | import chisel3.util.log2Up 7 | import dsptools.{DspException, Mod} 8 | 9 | /** Ctrl locations: 10 | * External = Use external Ctrl signal 11 | * Internal = Use interal Ctrl signal (i.e. wrap when maxed out) 12 | * TieFalse = Fix Ctrl signal to false 13 | * TieTrue = Fix Ctrl signal to true 14 | */ 15 | abstract class CtrlLoc 16 | case object External extends CtrlLoc 17 | case object Internal extends CtrlLoc 18 | case object TieFalse extends CtrlLoc 19 | case object TieTrue extends CtrlLoc 20 | 21 | /** Count type: 22 | * Up = always count up (count + inc) 23 | * Down = always count down (count - inc) 24 | * UpDown = count up/down (ctrl signal required) 25 | * UpMod = always count up, but mod with # 26 | */ 27 | abstract class CountType 28 | case object Up extends CountType 29 | case object Down extends CountType 30 | case object UpDown extends CountType 31 | case object UpMod extends CountType 32 | 33 | /** Counter Generator parameters */ 34 | case class CountParams ( 35 | countMax: Int, // Upper limit of counter range 36 | incMax: Int = 1, // Upper limit of increment range 37 | resetVal: Int = 0, // Value on reset 38 | wrapCtrl: CtrlLoc = Internal, // Location of wrap control signal 39 | changeCtrl: CtrlLoc = External, // Location of counter update control signal 40 | countType: CountType = Up, // Count type/direction 41 | customWrap: Boolean = false, // Whether custom wrap to value exists 42 | inputDelay: Int = 0 // Keep track of accumulated delay until module inputs 43 | ){ 44 | require (inputDelay >= 0, "Input delay must be non-negative") 45 | require (countMax >= 0, "Max counter value must be non-negative") 46 | require (resetVal >= 0 && resetVal <= countMax, "Counter reset should be [0,countMax]") 47 | require (incMax > 0 && incMax <= countMax, "Counter increment should be (0,countMax]") 48 | require (wrapCtrl != TieTrue, "Can't always wrap") 49 | require (changeCtrl == External || changeCtrl == TieTrue, "Either update on external signal or always update") 50 | require (!((countType == UpDown || countType == Down) && (incMax > 1) && (!customWrap || wrapCtrl == Internal)), 51 | "You must use a custom wrap condition and wrap to value if your counter delta is > 1" 52 | + " and you are possibly counting down") 53 | require (!(countType == Up && incMax > 1 && wrapCtrl == External && !customWrap), 54 | "When using an up counter with increment > 1, an external wrap condition cannot be used to trigger" 55 | + " counter to wrap to some __ internally defined value") 56 | } 57 | 58 | /** Counter control signals (I --> O can be passed through chain of counters) */ 59 | class CountCtrl (countParams: CountParams) extends Bundle { 60 | val wrap = if (countParams.wrapCtrl == External) Some(Bool()) else None 61 | val change = if (countParams.changeCtrl == External) Some(Bool()) else None 62 | val reset = Bool() 63 | } 64 | 65 | /** Counter IO */ 66 | class CountIO (countParams: CountParams) extends Bundle { 67 | // Count up/down control signal 68 | val upDown = if (countParams.countType == UpDown) Some(Input(Bool())) else None 69 | // Counters usually increment by 1 70 | val inc = if (countParams.incMax != 1) Some(Input(UInt(log2Up(countParams.incMax + 1).W))) else None 71 | // Counter wrap to value (up counters default wrap to 0) 72 | val wrapTo = if (countParams.customWrap) Some(Input(UInt(log2Up(countParams.countMax + 1).W))) else None 73 | // Counter default wrap condition is when count is maxed out (so need to know max) 74 | val max = { 75 | if (countParams.wrapCtrl == Internal && countParams.countType != UpMod) { 76 | Some(Input(UInt(log2Up(countParams.countMax + 1).W))) 77 | } 78 | else { 79 | None 80 | } 81 | } 82 | // n in x%n 83 | val modN = if (countParams.countType == UpMod) Some(Input(UInt((countParams.countMax + 1).W))) else None 84 | val out = Output(UInt(countParams.countMax.W)) 85 | } 86 | 87 | /** Counter template */ 88 | abstract class Counter(countParams: CountParams) extends Module { 89 | 90 | val io = IO(new CountIO(countParams)) 91 | 92 | val iCtrl = new CountCtrl(countParams) 93 | val oCtrl = new CountCtrl(countParams).flip 94 | 95 | val inc = io.inc.getOrElse(1.U) 96 | val max = io.max.getOrElse(countParams.countMax.U) 97 | 98 | val eq0 = io.out === 0.U 99 | val eqMax = io.out === max 100 | 101 | val (upCustom, upCustomWrap) = Mod(io.out + inc, max + 1.U) 102 | val (modOut,overflow) = { 103 | if(io.modN.isEmpty) { 104 | (io.out + inc,false.B) 105 | } 106 | else { 107 | Mod(io.out + inc,io.modN.get) 108 | } 109 | } 110 | 111 | // Adapt wrap condition based off of type of counter if it isn't retrieved externally 112 | val wrap = countParams.wrapCtrl match { 113 | case Internal => 114 | countParams.countType match { 115 | case UpDown => Mux(io.upDown.get, eq0, eqMax) 116 | case Down => eq0 117 | case Up => 118 | // For >1 increments, custom wrap indicated by sum overflow on next count 119 | if (countParams.incMax > 1) upCustomWrap else eqMax 120 | case UpMod => overflow 121 | } 122 | case TieFalse => false.B 123 | case TieTrue => true.B 124 | case External => iCtrl.wrap.get 125 | case _ => 126 | throw DspException(s"unknown value for countParams.wrapCtrl ${countParams.wrapCtrl}") 127 | } 128 | 129 | // Adapt wrap to value based off of type of counter if it isn't retrieved externally 130 | val wrapTo = { 131 | io.wrapTo.getOrElse( 132 | countParams.countType match { 133 | case UpDown => Mux(io.upDown.get,max, 0.U) 134 | case Down => max 135 | case _ => 0.U 136 | } 137 | ) 138 | } 139 | 140 | // If incrementing by 1 or using external wrap signals, add normally 141 | // But if incrementing by >1 and using internal wrap signals, do add mod (max + 1) 142 | val up = { 143 | if (countParams.incMax == 1 || (countParams.wrapCtrl == External && countParams.customWrap)) { 144 | // (io.out + inc).shorten(countParams.countMax) TODO: figure out what was intended here 145 | io.out + inc 146 | } 147 | else { 148 | upCustom 149 | } 150 | } 151 | 152 | val down = io.out - inc 153 | 154 | val nextInSeq = countParams.countType match { 155 | case UpDown => Mux(io.upDown.get,down,up) 156 | case Up => up 157 | case Down => down 158 | case UpMod => modOut 159 | } 160 | 161 | // When only internal wrap signals are used, note that mods already produce appropriately wrapped counter values 162 | val nextCount = { 163 | if (countParams.wrapCtrl == Internal && (countParams.countType == UpMod || 164 | (countParams.countType == Up && countParams.incMax > 1 && !countParams.customWrap))) { 165 | nextInSeq 166 | // else Mux(wrap,wrapTo,nextInSeq) 167 | } 168 | else { 169 | Mux(wrap, wrapTo, nextInSeq) 170 | } 171 | } 172 | 173 | // Conditionally update (hold until update) or always update 174 | val newOnClk = countParams.changeCtrl match { 175 | case External => Mux(iCtrl.change.get,nextCount,io.out) 176 | case TieTrue => nextCount 177 | } 178 | 179 | val count = Mux(iCtrl.reset, countParams.resetVal.U, newOnClk) 180 | // io.out := count.reg() TODO: Figure out where reg should come from 181 | io.out := count 182 | 183 | // When counters are chained, subsequent counter increments when current counter wraps 184 | if (countParams.changeCtrl == External) oCtrl.change.get := wrap & iCtrl.change.get 185 | if (countParams.wrapCtrl == External) oCtrl.wrap.get := wrap 186 | oCtrl.reset := iCtrl.reset 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/TODO/move_to_utilities/counters/ShiftRegisterWithReset.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.counters 4 | 5 | import chisel3._ 6 | import chisel3.util.RegEnable 7 | 8 | object ShiftRegisterWithReset { 9 | 10 | /** Returns the n-cycle delayed version of the input signal. 11 | * 12 | * @param in input to delay 13 | * @param n number of cycles to delay 14 | * @param en enable the shift 15 | */ 16 | def apply[T <: Data](in: T, n: Int, reset: T, en: Bool = true.B): T = { 17 | // The order of tests reflects the expected use cases. 18 | if (n != 0) { 19 | RegEnable(apply(in, n - 1, reset, en), reset, en) 20 | } else { 21 | in 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/dspmath/ExtendedEuclid.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.dspmath 4 | 5 | object ExtendedEuclid { 6 | 7 | /** Extended Euclidean Algorithm 8 | * ax + by = gcd(a, b) 9 | * Inputs: a, b 10 | * Outputs: gcd, x, y 11 | */ 12 | def egcd(a: Int, b: Int): (Int, Int, Int) = { 13 | if (a == 0) { 14 | (b, 0, 1) 15 | } else { 16 | val (gcd, y, x) = egcd(b % a, a) 17 | (gcd, x - (b / a) * y, y) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/dspmath/Factorization.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.dspmath 4 | 5 | case class RadPow(rad: Int, pow: Int) { 6 | 7 | /** `r ^ p` */ 8 | def get: Int = BigInt(rad).pow(pow).toInt 9 | 10 | /** Factorize i.e. rad = 4, pow = 3 -> Seq(4, 4, 4) */ 11 | def factorize: Seq[Int] = Seq.fill(pow)(rad) 12 | } 13 | 14 | case class Factorization(supportedRadsUnsorted: Seq[Seq[Int]]) { 15 | 16 | /** Supported radices, MSD First */ 17 | private val supportedRads = supportedRadsUnsorted.map(_.sorted.reverse) 18 | 19 | /** Factor n into powers of supported radices and store RadPow i.e. r^p, separated by coprimes 20 | * i.e. if supportedRads = Seq(Seq(4, 2), Seq(3)), 21 | * output = Seq(Seq(RadPow(4, 5), RadPow(2, 1)), Seq(RadPow(3, 7))) 22 | * implies n = 4^5 * 2^1 * 3^7 23 | */ 24 | private def getRadPows(n: Int): Seq[Seq[RadPow]] = { 25 | // Test if n can be factored by each of the supported radices (mod = 0) 26 | // Count # of times it can be factored 27 | var unfactorized = n 28 | val radPows = for (primeGroup <- supportedRads) yield { 29 | for (rad <- primeGroup) yield { 30 | var (mod, pow) = (0, 0) 31 | while (mod == 0) { 32 | mod = unfactorized % rad 33 | if (mod == 0) { 34 | pow = pow + 1 35 | unfactorized = unfactorized / rad 36 | } 37 | } 38 | RadPow(rad, pow) 39 | } 40 | } 41 | // If n hasn't completely been factorized, then an unsupported radix is required 42 | require(unfactorized == 1, s"$n is invalid for supportedRads.") 43 | radPows 44 | } 45 | 46 | /** Factor n into powers of supported radices (flattened) 47 | * i.e. if supportedRads = Seq(Seq(4, 2), Seq(3)), 48 | * output = Seq(5, 1, 7) 49 | * implies `n = 4^5 * 2^1 * 3^7` 50 | * If supportedRads contains more radices than the ones used, a power of 0 will be 51 | * associated with the unused radices. 52 | */ 53 | def getPowsFlat(n: Int): Seq[Int] = { 54 | getRadPows(n).flatMap(_.map(_.pow)) 55 | } 56 | 57 | /** Break n into coprimes i.e. 58 | * n = 4^5 * 2^1 * 3^7 59 | * would result in Seq(4^5 * 2^1, 3^7) 60 | * If supportedRads contains more coprime groups than the ones used, 1 will be 61 | * associated with the unused groups. 62 | */ 63 | def getCoprimes(n: Int): Seq[Int] = { 64 | getRadPows(n).map(_.map(_.get).product) 65 | } 66 | 67 | /** Factorizes the coprime into digit radices (mixed radix) 68 | * i.e. n = 8 -> Seq(4, 2) 69 | * Note: there's no padding! 70 | */ 71 | def factorizeCoprime(n: Int): Seq[Int] = { 72 | // i.e. if supportedRads = Seq(Seq(4, 2), Seq(3)) and n = 8, 73 | // correspondingPrimeGroup = Seq(4, 2) 74 | val correspondingPrimeGroup = supportedRads.filter(n % _.min == 0) 75 | require(correspondingPrimeGroup.length == 1, "n (coprime) must not be divisible by other primes.") 76 | // Factorize coprime -- only correspondingPrimeGroup should actually add to factorization length 77 | getRadPows(n).flatten.flatMap(_.factorize) 78 | } 79 | 80 | /** Gets associated base prime for n (assuming n isn't divisible by other primes) 81 | * WARNING: Assumes supportedRads contains the base prime! 82 | */ 83 | def getBasePrime(n: Int): Int = { 84 | val primeTemp = supportedRads.map(_.min).filter(n % _ == 0) 85 | require(primeTemp.length == 1, "n should only be divisible by 1 prime") 86 | primeTemp.head 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/misc/BitWidth.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.misc 4 | 5 | object BitWidth { 6 | 7 | /** 8 | * Utility function that computes bits required for a number 9 | * 10 | * @param n number of interest 11 | * @return 12 | */ 13 | def computeBits(n: BigInt): Int = { 14 | n.bitLength + (if (n < 0) 1 else 0) 15 | } 16 | 17 | /** 18 | * return the smallest number of bits required to hold the given number in 19 | * an SInt 20 | * Note: positive numbers will get one minimum width one higher than would be 21 | * required for a UInt 22 | * 23 | * @param num number to find width for 24 | * @return minimum required bits for an SInt 25 | */ 26 | def requiredBitsForSInt(num: BigInt): Int = { 27 | if (num == BigInt(0) || num == -BigInt(1)) { 28 | 1 29 | } else { 30 | if (num < 0) { 31 | computeBits(num) 32 | } else { 33 | computeBits(num) + 1 34 | } 35 | } 36 | } 37 | 38 | def requiredBitsForSInt(low: BigInt, high: BigInt): Int = { 39 | requiredBitsForSInt(low).max(requiredBitsForSInt(high)) 40 | } 41 | 42 | /** 43 | * return the smallest number of bits required to hold the given number in 44 | * an UInt 45 | * Note: positive numbers will get one minimum width one higher than would be 46 | * required for a UInt 47 | * 48 | * @param num number to find width for 49 | * @return minimum required bits for an SInt 50 | */ 51 | def requiredBitsForUInt(num: BigInt): Int = { 52 | if (num == BigInt(0)) { 53 | 1 54 | } else { 55 | computeBits(num) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/misc/DspException.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | import chisel3.ChiselException 6 | 7 | case class DspException(message: String) extends ChiselException(message) {} 8 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/misc/DspTesterUtilities.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.misc 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import dsptools.DspException 8 | import dsptools.numbers.{DspComplex, DspReal} 9 | import chisel3.InstanceId 10 | 11 | //scalastyle:off cyclomatic.complexity method.length 12 | object DspTesterUtilities { 13 | 14 | // Converts signed Double's to their 2's complement BigInt equivalents (unsigned) 15 | // (totalWidth, fractionalWidth of some FixedPoint) 16 | def signedToBigIntUnsigned(x: Double, totalWidth: Int, fractionalWidth: Int): BigInt = { 17 | val bi = FixedPoint.toBigInt(x, fractionalWidth) 18 | val neg = bi < 0 19 | val neededWidth = bi.bitLength + 1 20 | require(neededWidth <= totalWidth, "Double -> BigInt width larger than total width allocated!") 21 | if (neg) { 22 | (BigInt(1) << totalWidth) + bi 23 | } else { 24 | bi 25 | } 26 | } 27 | 28 | // Redundant from chisel-testers 29 | // Converts unsigned BigInt to signed BigInt (width = width of Chisel data type) 30 | def signConvert(bigInt: BigInt, width: Int): BigInt = { 31 | require(bigInt >= 0, "signConvert assumes bigInt is >= 0!") 32 | // Since the bigInt is always unsigned, bitLength always gets the max # of bits required to represent bigInt 33 | val w = bigInt.bitLength.max(width) 34 | // Negative if MSB is set or in this case, ex: 3 bit wide: negative if >= 4 35 | if (bigInt >= (BigInt(1) << (w - 1))) (bigInt - (BigInt(1) << w)) else bigInt 36 | } 37 | 38 | // Converts a positive 2's complement BigInt to a Double - used for FixedPoint 39 | def toDoubleFromUnsigned(i: BigInt, totalWidth: Int, fractionalWidth: Int): Double = { 40 | val signedBigInt = signConvert(i, totalWidth) 41 | FixedPoint.toDouble(signedBigInt, fractionalWidth) 42 | } 43 | 44 | // For DspReal represented as BigInt from Double (unsigned) 45 | def doubleToBigIntBits(double: Double): BigInt = { 46 | val ret = BigInt(java.lang.Double.doubleToLongBits(double)) 47 | if (ret >= 0) ret 48 | else (BigInt(1) << DspReal.underlyingWidth) + ret 49 | } 50 | 51 | // For DspReal represented as BigInt back to Double 52 | def bigIntBitsToDouble(bigInt: BigInt): Double = { 53 | java.lang.Double.longBitsToDouble(bigInt.toLong) 54 | } 55 | 56 | // Used to get signal name for printing to console 57 | private[dsptools] def getName(signal: InstanceId): String = { 58 | s"${signal.pathName}" 59 | } 60 | 61 | // Note: DspReal underlying is UInt 62 | // Checks if a basic number is signed or unsigned 63 | def isSigned(e: Data): Boolean = { 64 | e match { 65 | case _: SInt | _: FixedPoint => true 66 | case _: DspReal | _: Bool | _: UInt => false 67 | // Clock isn't a number, but it's still valid IO (should be treated as a Bool) 68 | case _: Clock => false 69 | case _ => throw DspException("Not a basic number/clock type! " + e) 70 | } 71 | } 72 | 73 | // For printing to Verilog testbench (signed) 74 | private[dsptools] def signPrefix(e: Element): String = { 75 | def signed = isSigned(e) 76 | if (signed) " signed " 77 | else "" 78 | } 79 | 80 | // Determines if peek/poke data fits in bit width 81 | def validRangeTest(signal: Data, value: BigInt): Unit = { 82 | val len = value.bitLength 83 | val neededLen = if (isSigned(signal)) len + 1 else len 84 | require(signal.widthOption.nonEmpty, "Cannot check range of node with unknown width!") 85 | if (neededLen > signal.getWidth) 86 | throw DspException(s"Value: $value is not in node ${getName(signal)} range") 87 | if (!isSigned(signal) && value < 0) 88 | throw DspException("Negative value can't be used with unsigned") 89 | } 90 | 91 | // Gets information on bitwidth, binarypoint for printing in console 92 | def bitInfo(signal: Data): String = signal.widthOption match { 93 | case Some(width) => { 94 | signal match { 95 | case f: FixedPoint => 96 | f.binaryPoint match { 97 | // Q integer . fractional bits 98 | case KnownBinaryPoint(bp) => s"Q${width - 1 - bp}.$bp" 99 | case _ => s"${width}-bit F" 100 | } 101 | case r: DspReal => "R" 102 | case u: UInt => s"${width}-bit U" 103 | case s: SInt => s"${width}-bit S" 104 | case c: DspComplex[_] => { 105 | val realInfo = bitInfo(c.real.asInstanceOf[Data]) 106 | val imagInfo = bitInfo(c.imag.asInstanceOf[Data]) 107 | s"[$realInfo, $imagInfo]" 108 | } 109 | case _ => throw DspException("Can't get bit info! Invalid type!") 110 | } 111 | } 112 | case None => "" 113 | } 114 | 115 | // Round value if data type is integer 116 | def roundData(data: Data, value: Double): Double = { 117 | data match { 118 | case _: SInt | _: UInt => value.round.toDouble 119 | case _: DspReal | _: FixedPoint => value 120 | case _ => throw DspException("Invalid data type for rounding determination") 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/misc/PeekPokeDspExtensions.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.misc 4 | 5 | import breeze.math.Complex 6 | import chisel3._ 7 | import fixedpoint._ 8 | import chiseltest.iotesters.PeekPokeTester 9 | import dsptools.DspException 10 | import dsptools.misc.DspTesterUtilities.{getName, roundData, validRangeTest} 11 | import dsptools.numbers._ 12 | 13 | trait PeekPokeDspExtensions { 14 | this: PeekPokeTester[_] => 15 | 16 | private def dspPeek(node: Data): (Double, BigInt) = { 17 | val bi: BigInt = node match { 18 | // Unsigned bigint 19 | case r: DspReal => peek(r.node.asInstanceOf[Bits]) 20 | case b: Bits => peek(b.asInstanceOf[Bits]) 21 | case f: FixedPoint => peek(f.asSInt.asInstanceOf[Bits]) 22 | } 23 | val (dblOut, bigIntOut) = node match { 24 | case _: DspReal => (DspTesterUtilities.bigIntBitsToDouble(bi), bi) 25 | case f: FixedPoint => 26 | f.binaryPoint match { 27 | case KnownBinaryPoint(bp) => (FixedPoint.toDouble(bi, bp), bi) 28 | case _ => throw DspException("Cannot peek FixedPoint with unknown binary point location") 29 | } 30 | // UInt + SInt = Bits 31 | case _: Bits => (bi.doubleValue, bi) 32 | case _ => throw DspException(s"Peeked node ${getName(node)} has incorrect type ${node.getClass.getName}") 33 | } 34 | (dblOut, bigIntOut) 35 | } 36 | 37 | def peek(node: FixedPoint): Double = dspPeek(node)._1 38 | 39 | // Takes precedence over Aggregate 40 | def peek(node: DspReal): Double = dspPeek(node)._1 41 | 42 | // General type returns Double 43 | def peek(node: Data): Double = dspPeek(node)._1 44 | 45 | def peek(c: DspComplex[_]): Complex = { 46 | Complex(dspPeek(c.real.asInstanceOf[Data])._1, dspPeek(c.imag.asInstanceOf[Data])._1) 47 | } 48 | 49 | def poke(signal: FixedPoint, value: Int): Unit = poke(signal, value.toDouble) 50 | 51 | def poke(signal: FixedPoint, value: Double): Unit = poke(signal.asInstanceOf[Data], value) 52 | 53 | // DspReal extends Bundle extends Aggregate extends Data 54 | // If poking DspReal with Double, can only go here 55 | // Type classes are all Data:RealBits 56 | //scalastyle:off cyclomatic.complexity 57 | def poke(signal: Data, value: Double): Unit = { 58 | signal match { 59 | case f: FixedPoint => 60 | f.binaryPoint match { 61 | case KnownBinaryPoint(bp) => 62 | poke(f.asSInt.asInstanceOf[Bits], FixedPoint.toBigInt(value, bp)) 63 | case _ => throw DspException("Must poke FixedPoint with known binary point") 64 | } 65 | case r: DspReal => poke(r.node.asInstanceOf[Bits], DspTesterUtilities.doubleToBigIntBits(value)) 66 | // UInt + SInt 67 | case b: Bits => poke(b.asInstanceOf[Bits], BigInt(value.round.toInt)) 68 | case _ => throw DspException("Illegal poke value for node of type Data and value of type Double") 69 | } 70 | } 71 | 72 | def poke(signal: Data, value: BigDecimal): Unit = { 73 | assert(value <= Double.MaxValue, s"poking ${signal} with a value $value bigger than Double.MaxValue") 74 | poke(signal, value.toDouble) 75 | } 76 | 77 | def poke(c: DspComplex[_], value: Complex): Unit = { 78 | poke(c.real.asInstanceOf[Data], value.real) 79 | poke(c.imag.asInstanceOf[Data], value.imag) 80 | } 81 | 82 | def pokeFixedPoint(signal: FixedPoint, value: Double): Unit = { 83 | poke(signal, value) 84 | } 85 | 86 | def pokeFixedPointBig(signal: FixedPoint, value: BigDecimal): Unit = { 87 | poke(signal, value) 88 | } 89 | 90 | def checkDecimal(data: Data, expected: Double, dblVal: Double, bitVal: BigInt): (Boolean, Double) = { 91 | def toMax(w: Int): BigInt = (BigInt(1) << w) - 1 92 | 93 | // <= 94 | val fixTol = 0 95 | val realTol = 8 96 | val fixTolInt = toMax(fixTol) 97 | val floTolDec = math.pow(10, -realTol) 98 | // Error checking does a bad job of handling really small numbers, 99 | // so let's just force the really small numbers to 0 100 | val expected0 = if (math.abs(expected) < floTolDec / 100) 0.0 else expected 101 | val dblVal0 = if (math.abs(dblVal) < floTolDec / 100) 0.0 else dblVal 102 | val expectedBits = data match { 103 | case _: DspReal => DspTesterUtilities.doubleToBigIntBits(expected0) // unsigned BigInt 104 | case f: FixedPoint => 105 | f.binaryPoint match { 106 | case KnownBinaryPoint(bp) => FixedPoint.toBigInt(expected0, bp) 107 | case _ => throw DspException("Unknown binary point in FixedPoint on expect") 108 | } 109 | case _: Bits => BigInt(expected0.round.toInt) 110 | } 111 | 112 | validRangeTest(data, expectedBits) 113 | 114 | // Allow for some tolerance in error checking 115 | val (tolerance, tolDec) = data match { 116 | case f: FixedPoint => 117 | f.binaryPoint match { 118 | case KnownBinaryPoint(bp) => (fixTolInt, FixedPoint.toDouble(fixTolInt, bp)) 119 | case _ => throw DspException("Unknown binary point!") 120 | } 121 | case _: SInt | _: UInt => (fixTolInt, fixTolInt.toDouble) 122 | case _: DspReal => (DspTesterUtilities.doubleToBigIntBits(floTolDec), floTolDec) 123 | } 124 | val good = { 125 | if (dblVal0 != expected0) { 126 | val gotDiffDbl = math.abs(dblVal0 - expected0) 127 | val gotDiffBits = (bitVal - expectedBits).abs 128 | val passDbl = gotDiffDbl <= tolDec 129 | val passBits = gotDiffBits <= tolerance 130 | passDbl && passBits 131 | } else { 132 | true 133 | } 134 | } 135 | (good, tolDec) 136 | } 137 | 138 | // Expect on DspReal goes straight to here 139 | def expect(data: Data, expected: Double): Boolean = expect(data, expected, msg = "") 140 | 141 | def expectWithoutFailure(data: Data, expected: Double, msg: String = ""): Boolean = { 142 | val expectedNew = roundData(data, expected) 143 | val path = getName(data) 144 | val (dblVal, bitVal) = dspPeek(data) 145 | val (good, tolerance) = checkDecimal(data, expectedNew, dblVal, bitVal) 146 | good 147 | } 148 | 149 | def expect(data: Data, expected: Double, msg: String): Boolean = { 150 | val good = expectWithoutFailure(data, expected, msg) 151 | expect(good, msg) 152 | } 153 | 154 | def expect(signal: FixedPoint, expected: Int): Boolean = expect(signal, expected, "") 155 | 156 | def expect(signal: FixedPoint, expected: Int, msg: String): Boolean = expect(signal, expected.toDouble, msg) 157 | 158 | def expect(signal: FixedPoint, expected: Double): Boolean = expect(signal, expected, "") 159 | 160 | def expect(signal: FixedPoint, expected: Double, msg: String): Boolean = { 161 | expect(signal.asInstanceOf[Data], expected, msg) 162 | } 163 | 164 | def expect(data: Data, expected: BigDecimal): Boolean = expect(data, expected, "") 165 | 166 | def expect(data: Data, expected: BigDecimal, msg: String): Boolean = { 167 | assert(expected <= Double.MaxValue, s"expecting from ${data} a value $expected that is bigger than Double.MaxValue") 168 | val good = expectWithoutFailure(data, expected.toDouble, msg) 169 | expect(good, msg) 170 | } 171 | 172 | def expect(data: DspComplex[_], expected: Complex): Boolean = expect(data, expected, msg = "") 173 | 174 | def expect(data: DspComplex[_], expected: Complex, msg: String): Boolean = { 175 | val dataReal = data.real.asInstanceOf[Data] 176 | val dataImag = data.imag.asInstanceOf[Data] 177 | val expectedNewR = roundData(dataReal, expected.real) 178 | val expectedNewI = roundData(dataImag, expected.imag) 179 | val path = getName(data) 180 | val (good, dblValR, dblValI, toleranceR) = { 181 | val (dblValR, bitValR) = dspPeek(dataReal) 182 | val (dblValI, bitValI) = dspPeek(dataImag) 183 | val (goodR, toleranceR) = checkDecimal(dataReal, expectedNewR, dblValR, bitValR) 184 | val (goodI, _) = checkDecimal(dataImag, expectedNewI, dblValI, bitValI) 185 | (goodR & goodI, dblValR, dblValI, toleranceR) 186 | } 187 | expect(good, msg) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/DspContext.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | import scala.util.DynamicVariable 6 | 7 | /** Different overflow handling methods */ 8 | trait OverflowType 9 | case object Saturate extends OverflowType 10 | case object Wrap extends OverflowType 11 | case object Grow extends OverflowType 12 | 13 | /** Different trim methods */ 14 | abstract class TrimType 15 | case object Truncate extends TrimType 16 | case object NoTrim extends TrimType 17 | case object RoundDown extends TrimType 18 | case object RoundUp extends TrimType 19 | case object RoundTowardsZero extends TrimType 20 | case object RoundTowardsInfinity extends TrimType 21 | case object RoundHalfDown extends TrimType 22 | case object RoundHalfUp extends TrimType 23 | case object RoundHalfTowardsZero extends TrimType 24 | case object RoundHalfTowardsInfinity extends TrimType 25 | case object RoundHalfToEven extends TrimType 26 | case object RoundHalfToOdd extends TrimType 27 | 28 | object DspContext { 29 | 30 | val defaultOverflowType = Grow 31 | val defaultTrimType = RoundDown 32 | val defaultBinaryPointGrowth = 1 33 | val defaultBinaryPoint = Some(14) 34 | val defaultNumBits = Some(16) 35 | val defaultComplexUse4Muls = false 36 | val defaultNumMulPipes = 0 37 | val defaultNumAddPipes = 0 38 | 39 | private val dynamicDspContextVar = new DynamicVariable[DspContext](new DspContext()) 40 | 41 | def current: DspContext = dynamicDspContextVar.value 42 | 43 | def alter[T](newContext: DspContext)(blk: => T): T = { 44 | dynamicDspContextVar.withValue(newContext) { 45 | blk 46 | } 47 | } 48 | 49 | def withBinaryPoint[T](newBinaryPoint: Int)(blk: => T): T = { 50 | dynamicDspContextVar.withValue(current.copy(binaryPoint = Some(newBinaryPoint))) { 51 | blk 52 | } 53 | } 54 | 55 | def withNumBits[T](newNumBits: Int)(blk: => T): T = { 56 | dynamicDspContextVar.withValue(current.copy(numBits = Some(newNumBits))) { 57 | blk 58 | } 59 | } 60 | 61 | def withComplexUse4Muls[T](newComplexUse4Muls: Boolean)(blk: => T): T = { 62 | dynamicDspContextVar.withValue(current.copy(complexUse4Muls = newComplexUse4Muls)) { 63 | blk 64 | } 65 | } 66 | 67 | def withOverflowType[T](newOverflowType: OverflowType)(blk: => T): T = { 68 | dynamicDspContextVar.withValue(current.copy(overflowType = newOverflowType)) { 69 | blk 70 | } 71 | } 72 | 73 | def withTrimType[T](newTrimType: TrimType)(blk: => T): T = { 74 | dynamicDspContextVar.withValue(current.copy(trimType = newTrimType)) { 75 | blk 76 | } 77 | } 78 | 79 | def withBinaryPointGrowth[T](newBinaryPointGrowth: Int)(blk: => T): T = { 80 | dynamicDspContextVar.withValue(current.copy(binaryPointGrowth = newBinaryPointGrowth)) { 81 | blk 82 | } 83 | } 84 | 85 | def withNumMulPipes[T](newNumMulPipes: Int)(blk: => T): T = { 86 | dynamicDspContextVar.withValue(current.copy(numMulPipes = newNumMulPipes)) { 87 | blk 88 | } 89 | } 90 | 91 | def withNumAddPipes[T](newNumAddPipes: Int)(blk: => T): T = { 92 | dynamicDspContextVar.withValue(current.copy(numAddPipes = newNumAddPipes)) { 93 | blk 94 | } 95 | } 96 | 97 | } 98 | 99 | trait hasContext extends Any { 100 | def context: DspContext = DspContext.current 101 | } 102 | 103 | case class DspContext( 104 | val overflowType: OverflowType = DspContext.defaultOverflowType, 105 | val trimType: TrimType = DspContext.defaultTrimType, 106 | val binaryPoint: Option[Int] = DspContext.defaultBinaryPoint, 107 | val numBits: Option[Int] = DspContext.defaultNumBits, 108 | val complexUse4Muls: Boolean = DspContext.defaultComplexUse4Muls, 109 | val numMulPipes: Int = DspContext.defaultNumMulPipes, 110 | val numAddPipes: Int = DspContext.defaultNumAddPipes, 111 | val binaryPointGrowth: Int = DspContext.defaultBinaryPointGrowth) { 112 | 113 | require(numMulPipes >= 0, "# of pipeline registers for multiplication must be >= 0 ") 114 | require(numAddPipes >= 0, "# of pipeline registers for addition must be >= 0 ") 115 | require(binaryPointGrowth >= 0, "Binary point growth must be non-negative") 116 | numBits match { 117 | case Some(i) => require(i > 0, "# of bits must be > 0") 118 | case _ => 119 | } 120 | 121 | def complexMulPipe: Int = { 122 | if (complexUse4Muls) numMulPipes + numAddPipes 123 | else (2 * numAddPipes) + numMulPipes 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/Eq.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.{Bool, Data} 6 | 7 | /** 8 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 9 | * Java Bools. I suppose a more general solution would be generic in 10 | * return type, but the use cases there seem obscure. 11 | */ 12 | 13 | /** 14 | * A type class used to determine equality between 2 instances of the same 15 | * type. Any 2 instances `x` and `y` are equal if `eqv(x, y)` is `true`. 16 | * Moreover, `eqv` should form an equivalence relation. 17 | */ 18 | trait Eq[A <: Data] extends Any { 19 | 20 | /** Returns `true` if `x` and `y` are equivalent, `false` otherwise. */ 21 | def eqv(x: A, y: A): Bool 22 | 23 | /** Returns `false` if `x` and `y` are equivalent, `true` otherwise. */ 24 | def neqv(x: A, y: A): Bool = !eqv(x, y) 25 | 26 | /** 27 | * Constructs a new `Eq` instance for type `B` where 2 elements are 28 | * equivalent iff `eqv(f(x), f(y))`. 29 | */ 30 | def on[B <: Data](f: B => A): Eq[B] = new MappedEq(this)(f) 31 | } 32 | 33 | private[numbers] class MappedEq[A <: Data, B <: Data](eq: Eq[B])(f: A => B) extends Eq[A] { 34 | def eqv(x: A, y: A): Bool = eq.eqv(f(x), f(y)) 35 | } 36 | 37 | object Eq { 38 | def apply[A <: Data](implicit e: Eq[A]): Eq[A] = e 39 | 40 | def by[A <: Data, B <: Data](f: A => B)(implicit e: Eq[B]): Eq[A] = new MappedEq(e)(f) 41 | } 42 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/Order.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chisel3.util.ValidIO 7 | 8 | /** 9 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 10 | * Java Bools. I suppose a more general solution would be generic in 11 | * return type, but the use cases there seem obscure. 12 | */ 13 | 14 | /** 15 | * The `Order` type class is used to define a total ordering on some type `A`. 16 | * An order is defined by a relation <=, which obeys the following laws: 17 | * 18 | * - either x <= y or y <= x (totality) 19 | * - if x <= y and y <= x, then x == y (antisymmetry) 20 | * - if x <= y and y <= z, then x <= z (transitivity) 21 | * 22 | * The truth table for compare is defined as follows: 23 | * 24 | * x <= y x >= y Int 25 | * true true = 0 (corresponds to x == y) 26 | * true false < 0 (corresponds to x < y) 27 | * false true > 0 (corresponds to x > y) 28 | * 29 | * By the totality law, x <= y and y <= x cannot be both false. 30 | */ 31 | trait Order[A <: Data] extends Any with PartialOrder[A] { 32 | self => 33 | 34 | def partialCompare(x: A, y: A): ValidIO[ComparisonBundle] = { 35 | val c = compare(x, y) 36 | // Always valid 37 | ComparisonHelper(true.B, c.eq, c.lt) 38 | } 39 | 40 | override def eqv(x: A, y: A): Bool = compare(x, y).eq 41 | override def gt(x: A, y: A): Bool = { 42 | val c = compare(x, y) 43 | !(c.eq || c.lt) 44 | } 45 | override def lt(x: A, y: A): Bool = compare(x, y).lt 46 | override def gteqv(x: A, y: A): Bool = { 47 | val c = compare(x, y) 48 | c.eq || (!c.lt) 49 | } 50 | override def lteqv(x: A, y: A): Bool = { 51 | val c = compare(x, y) 52 | c.lt || c.eq 53 | } 54 | 55 | def min(x: A, y: A): A = fixedpoint.shadow.Mux(lt(x, y), x, y) 56 | def max(x: A, y: A): A = fixedpoint.shadow.Mux(gt(x, y), x, y) 57 | def compare(x: A, y: A): ComparisonBundle 58 | 59 | /** 60 | * Defines an order on `B` by mapping `B` to `A` using `f` and using `A`s 61 | * order to order `B`. 62 | */ 63 | override def on[B <: Data](f: B => A): Order[B] = new MappedOrder(this)(f) 64 | 65 | /** 66 | * Defines an ordering on `A` where all arrows switch direction. 67 | */ 68 | override def reverse: Order[A] = new ReversedOrder(this) 69 | } 70 | 71 | private[numbers] class MappedOrder[A <: Data, B <: Data](order: Order[B])(f: A => B) extends Order[A] { 72 | def compare(x: A, y: A): ComparisonBundle = order.compare(f(x), f(y)) 73 | } 74 | 75 | private[numbers] class ReversedOrder[A <: Data](order: Order[A]) extends Order[A] { 76 | def compare(x: A, y: A): ComparisonBundle = order.compare(y, x) 77 | } 78 | 79 | object Order { 80 | @inline final def apply[A <: Data](implicit o: Order[A]): Order[A] = o 81 | 82 | def by[A <: Data, B <: Data](f: A => B)(implicit o: Order[B]): Order[A] = o.on(f) 83 | 84 | def from[A <: Data](f: (A, A) => ComparisonBundle): Order[A] = new Order[A] { 85 | def compare(x: A, y: A): ComparisonBundle = f(x, y) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/PartialOrder.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.util.{Valid, ValidIO} 6 | import chisel3.{Bool, Data} 7 | 8 | // Note: For type classing normal Chisel number data types like UInt, SInt, FixedPoint, etc. 9 | // you should *not* have to rely on PartialOrder (all comparisons to the same type are legal) 10 | // Therefore, in their "top-level" type classes, you should be overriding eqv, lteqv, lt, gteqv, gt 11 | 12 | /** 13 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 14 | * Java Bools. I suppose a more general solution would be generic in 15 | * return type, but the use cases there seem obscure. 16 | */ 17 | 18 | /** 19 | * The `PartialOrder` type class is used to define a partial ordering on some type `A`. 20 | * 21 | * A partial order is defined by a relation <=, which obeys the following laws: 22 | * 23 | * - x <= x (reflexivity) 24 | * - if x <= y and y <= x, then x === y (anti-symmetry) 25 | * - if x <= y and y <= z, then x <= z (transitivity) 26 | * 27 | * To compute both <= and >= at the same time, we use a Double number 28 | * to encode the result of the comparisons x <= y and x >= y. 29 | * The truth table is defined as follows: 30 | * 31 | * x <= y x >= y Double 32 | * true true = 0.0 (corresponds to x === y) 33 | * false false = NaN (x and y cannot be compared) 34 | * true false = -1.0 (corresponds to x < y) 35 | * false true = 1.0 (corresponds to x > y) 36 | */ 37 | trait PartialOrder[A <: Data] extends Any with Eq[A] { 38 | self => 39 | 40 | /** Result of comparing `x` with `y`. Returns ValidIO[ComparisonBundle] 41 | * with `valid` false if operands are not comparable. If operands are 42 | * comparable, `bits.lt` will be true if `x` < `y` and `bits.eq` will 43 | * be true if `x` = `y`` 44 | */ 45 | def partialCompare(x: A, y: A): ValidIO[ComparisonBundle] 46 | 47 | /** Result of comparing `x` with `y`. Returns None if operands 48 | * are not comparable. If operands are comparable, returns Some[Int] 49 | * where the Int sign is: 50 | * - negative iff `x < y` 51 | * - zero iff `x == y` 52 | * - positive iff `x > y` 53 | */ 54 | 55 | /** Returns Some(x) if x <= y, Some(y) if x > y, otherwise None. */ 56 | def pmin(x: A, y: A): ValidIO[A] = { 57 | val c = partialCompare(x, y) 58 | val value = fixedpoint.shadow.Mux(c.bits.lt, x, y) 59 | val ret = Valid(value) 60 | ret.valid := c.valid 61 | ret 62 | } 63 | 64 | /** Returns Some(x) if x >= y, Some(y) if x < y, otherwise None. */ 65 | def pmax(x: A, y: A): ValidIO[A] = { 66 | val c = partialCompare(x, y) 67 | val value = fixedpoint.shadow.Mux(!c.bits.lt, x, y) 68 | val ret = Valid(value) 69 | ret.valid := c.valid 70 | ret 71 | } 72 | 73 | // The following should be overriden in priority for performance 74 | def eqv(x: A, y: A): Bool = { 75 | val c = partialCompare(x, y) 76 | c.bits.eq && c.valid 77 | } 78 | def lteqv(x: A, y: A): Bool = { 79 | val c = partialCompare(x, y) 80 | (c.bits.lt || c.bits.eq) && c.valid 81 | } 82 | def lt(x: A, y: A): Bool = { 83 | val c = partialCompare(x, y) 84 | c.bits.lt && c.valid 85 | } 86 | 87 | def gteqv(x: A, y: A): Bool = lteqv(y, x) 88 | def gt(x: A, y: A): Bool = lt(y, x) 89 | 90 | /** 91 | * Defines a partial order on `B` by mapping `B` to `A` using `f` and using `A`s 92 | * order to order `B`. 93 | */ 94 | override def on[B <: Data](f: B => A): PartialOrder[B] = new MappedPartialOrder(this)(f) 95 | 96 | /** 97 | * Defines a partial order on `A` where all arrows switch direction. 98 | */ 99 | def reverse: PartialOrder[A] = new ReversedPartialOrder(this) 100 | } 101 | 102 | private[numbers] class MappedPartialOrder[A <: Data, B <: Data](partialOrder: PartialOrder[B])(f: A => B) 103 | extends PartialOrder[A] { 104 | def partialCompare(x: A, y: A): ValidIO[ComparisonBundle] = partialOrder.partialCompare(f(x), f(y)) 105 | } 106 | 107 | private[numbers] class ReversedPartialOrder[A <: Data](partialOrder: PartialOrder[A]) extends PartialOrder[A] { 108 | def partialCompare(x: A, y: A): ValidIO[ComparisonBundle] = partialOrder.partialCompare(y, x) 109 | } 110 | 111 | object PartialOrder { 112 | @inline final def apply[A <: Data](implicit po: PartialOrder[A]): PartialOrder[A] = po 113 | 114 | def by[A <: Data, B <: Data](f: A => B)(implicit po: PartialOrder[B]): PartialOrder[A] = po.on(f) 115 | 116 | def from[A <: Data](f: (A, A) => ValidIO[ComparisonBundle]): PartialOrder[A] = new PartialOrder[A] { 117 | def partialCompare(x: A, y: A): ValidIO[ComparisonBundle] = f(x, y) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/Ring.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.Data 6 | 7 | /* Needs to be redefined from spire */ 8 | object Ring { 9 | def apply[A <: Data](implicit A: Ring[A]): Ring[A] = A 10 | } 11 | 12 | trait Ring[A] extends Any with spire.algebra.Ring[A] { 13 | def plusContext(f: A, g: A): A 14 | def minusContext(f: A, g: A): A 15 | def timesContext(f: A, g: A): A 16 | def negateContext(f: A): A 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/Signed.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.util.ShiftRegister 6 | import chisel3.{Bool, Data} 7 | import dsptools.hasContext 8 | 9 | /** 10 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 11 | * Java Bools. I suppose a more general solution would be generic in 12 | * return type, but the use cases there seem obscure. 13 | */ 14 | 15 | /** 16 | * A trait for things that have some notion of sign and the ability to ensure 17 | * something has a positive sign. 18 | */ 19 | trait Signed[A] extends Any { 20 | 21 | /** Returns Zero if `a` is 0, Positive if `a` is positive, and Negative is `a` is negative. */ 22 | def sign(a: A): Sign = Sign(signum(a)) 23 | 24 | /** Returns 0 if `a` is 0, > 0 if `a` is positive, and < 0 is `a` is negative. */ 25 | def signum(a: A): ComparisonBundle 26 | 27 | /** An idempotent function that ensures an object has a non-negative sign. */ 28 | def abs(a: A): A 29 | //noinspection ScalaStyle 30 | def context_abs(a: A): A 31 | 32 | def isSignZero(a: A): Bool = signum(a).eq 33 | def isSignPositive(a: A): Bool = !isSignZero(a) && !isSignNegative(a) 34 | def isSignNegative(a: A): Bool = signum(a).lt 35 | 36 | def isSignNonZero(a: A): Bool = !isSignZero(a) 37 | def isSignNonPositive(a: A): Bool = !isSignPositive(a) 38 | def isSignNonNegative(a: A): Bool = !isSignNegative(a) 39 | 40 | } 41 | 42 | object Signed { 43 | implicit def orderedRingIsSigned[A <: Data: Order: Ring]: Signed[A] = new OrderedRingIsSigned[A] 44 | 45 | def apply[A <: Data](implicit s: Signed[A]): Signed[A] = s 46 | } 47 | 48 | //scalastyle:off method.name 49 | private class OrderedRingIsSigned[A <: Data](implicit o: Order[A], r: Ring[A]) extends Signed[A] with hasContext { 50 | def signum(a: A): ComparisonBundle = o.compare(a, r.zero) 51 | def abs(a: A): A = { 52 | fixedpoint.shadow.Mux(signum(a).lt, r.negate(a), a) 53 | } 54 | def context_abs(a: A): A = { 55 | fixedpoint.shadow.Mux( 56 | signum(ShiftRegister(a, context.numAddPipes)).lt, 57 | r.negateContext(a), 58 | ShiftRegister(a, context.numAddPipes) 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/helpers/Comparison.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.{Bool, Bundle, Wire} 6 | import chisel3.util.{Valid, ValidIO} 7 | 8 | // Helper bundles that theoretically aren't needed unless you wan't to be super general 9 | class ComparisonBundle extends Bundle { 10 | val eq = Bool() 11 | // ignore lt if eq is true 12 | val lt = Bool() 13 | } 14 | 15 | // Note: Only useful with Partial Order (when comparisons might not be valid) 16 | object ComparisonHelper { 17 | def apply(valid: Bool, eq: Bool, lt: Bool): ValidIO[ComparisonBundle] = { 18 | val ret = Wire(Valid(new ComparisonBundle().cloneType)) 19 | ret.bits.eq := eq 20 | ret.bits.lt := lt 21 | ret.valid := valid 22 | ret 23 | } 24 | def apply(eq: Bool, lt: Bool): ComparisonBundle = { 25 | val ret = Wire(new ComparisonBundle().cloneType) 26 | ret.eq := eq 27 | ret.lt := lt 28 | ret 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/algebra_types/helpers/Sign.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chisel3.util.ShiftRegister 7 | import dsptools.hasContext 8 | 9 | import scala.language.implicitConversions 10 | 11 | /** 12 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 13 | * Java Bools. I suppose a more general solution would be generic in 14 | * return type, but the use cases there seem obscure. 15 | */ 16 | 17 | /** 18 | * A simple ADT representing the `Sign` of an object. 19 | */ 20 | sealed class Sign(zeroInit: Option[Boolean] = None, negInit: Option[Boolean] = None) extends Bundle { 21 | // import Sign._ 22 | val zero = zeroInit.map { _.B }.getOrElse(Bool()) 23 | // ignore neg if zero is true 24 | val neg = negInit.map { _.B }.getOrElse(Bool()) 25 | 26 | def unary_- : Sign = Sign(this.zero, !this.neg) 27 | 28 | def *(that: Sign): Sign = Sign( 29 | this.zero || that.zero, 30 | this.neg ^ that.neg 31 | ) 32 | 33 | def **(that: Int): Sign = { 34 | val evenPow = that % 2 == 0 35 | Sign(zero, if (evenPow) false.B else neg) 36 | } 37 | 38 | // LSB indicates even or oddness -- only negative if this is negative and 39 | // it's raised by an odd power 40 | def **(that: UInt): Sign = Sign(this.zero, this.neg && that(0)) 41 | } 42 | 43 | object Sign { 44 | case object Zero extends Sign(Some(true), Some(false)) 45 | case object Positive extends Sign(Some(false), Some(false)) 46 | case object Negative extends Sign(Some(false), Some(true)) 47 | 48 | def apply(zero: Bool, neg: Bool): Sign = { 49 | val zeroLit = zero.litOption.map { _ != BigInt(0) } 50 | val negLit = neg.litOption.map { _ != BigInt(0) } 51 | val isLit = zeroLit.isDefined && negLit.isDefined 52 | val wireWrapIfNotLit: Sign => Sign = s => 53 | if (isLit) { s } 54 | else Wire(s) 55 | val bundle = wireWrapIfNotLit( 56 | new Sign(zeroInit = zeroLit, negInit = negLit) 57 | ) 58 | if (!zero.isLit) { 59 | bundle.zero := zero 60 | } 61 | if (!neg.isLit) { 62 | bundle.neg := neg 63 | } 64 | bundle 65 | } 66 | 67 | implicit def apply(i: Int): Sign = 68 | if (i == 0) Zero else if (i > 0) Positive else Negative 69 | 70 | implicit def apply(i: ComparisonBundle): Sign = { 71 | Sign(i.eq, i.lt) 72 | } 73 | 74 | class SignAlgebra extends CMonoid[Sign] with Signed[Sign] with Order[Sign] { 75 | def empty: Sign = Positive 76 | def combine(a: Sign, b: Sign): Sign = a * b 77 | 78 | override def sign(a: Sign): Sign = a 79 | def signum(a: Sign): ComparisonBundle = ComparisonHelper(a.zero, a.neg) 80 | def abs(a: Sign): Sign = if (a == Negative) Positive else a 81 | def context_abs(a: Sign): Sign = if (a == Negative) Positive else a 82 | 83 | def compare(x: Sign, y: Sign): ComparisonBundle = { 84 | val eq = fixedpoint.shadow.Mux( 85 | x.zero, 86 | // if x is zero, y must also be zero for equality 87 | y.zero, 88 | // if x is not zero, y must not be zero and must have the same sign 89 | !y.zero && (x.neg === y.neg) 90 | ) 91 | // lt only needs to be correct when eq not true 92 | val lt = fixedpoint.shadow.Mux( 93 | x.zero, 94 | // if x is zero, then true when y positive 95 | !y.zero && !y.neg, 96 | // if x is not zero, then true when x is negative and y not negative 97 | x.neg && (y.zero || !y.neg) 98 | ) 99 | 100 | ComparisonHelper(eq, lt) 101 | } 102 | 103 | override def reverse: SignAlgebra = new SignAlgebra { 104 | override def compare(x: Sign, y: Sign): ComparisonBundle = super.compare(y, x) 105 | } 106 | } 107 | 108 | implicit final val SignAlgebra = new SignAlgebra 109 | 110 | implicit final val SignMultiplicativeGroup: MultiplicativeCMonoid[Sign] = 111 | Multiplicative(SignAlgebra) 112 | 113 | //scalastyle:off method.name 114 | implicit def SignAction[A <: Data](implicit A: AdditiveGroup[A]): MultiplicativeAction[A, Sign] = 115 | new MultiplicativeAction[A, Sign] with hasContext { 116 | // Multiply a # by a sign 117 | def gtimesl(s: Sign, a: A): A = { 118 | fixedpoint.shadow.Mux( 119 | ShiftRegister(s.zero, context.numAddPipes), 120 | ShiftRegister(A.zero, context.numAddPipes), 121 | fixedpoint.shadow 122 | .Mux(ShiftRegister(s.neg, context.numAddPipes), A.negate(a), ShiftRegister(a, context.numAddPipes)) 123 | ) 124 | } 125 | def gtimesr(a: A, s: Sign): A = gtimesl(s, a) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/binary_types/BinaryRepresentation.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.{Bool, Data, UInt} 6 | 7 | object BinaryRepresentation { 8 | def apply[A <: Data](implicit A: BinaryRepresentation[A]): BinaryRepresentation[A] = A 9 | } 10 | 11 | trait BinaryRepresentation[A <: Data] extends Any { 12 | def shl(a: A, n: Int): A 13 | def shl(a: A, n: UInt): A 14 | // For negative signed #'s, this is actually round to negative infinity 15 | def shr(a: A, n: Int): A 16 | def shr(a: A, n: UInt): A 17 | def signBit(a: A): Bool 18 | 19 | // Rounds to zero (positive, negative consistent!) 20 | // Divide/multiply by 2^n 21 | def div2(a: A, n: Int): A = shr(a, n) 22 | def mul2(a: A, n: Int): A = shl(a, n) 23 | // Trim to n fractional bits (with DspContext) -- doesn't affect DspReal 24 | def trimBinary(a: A, n: Int): A = trimBinary(a, Some(n)) 25 | def trimBinary(a: A, n: Option[Int]): A 26 | 27 | // Clip A to B (range) 28 | def clip(a: A, b: A): A 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/binary_types/NumberBits.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | 7 | trait RealBits[A <: Data] extends Any with Real[A] with ChiselConvertableFrom[A] with BinaryRepresentation[A] {} 8 | 9 | object RealBits { 10 | def apply[A <: Data](implicit A: RealBits[A]): RealBits[A] = A 11 | } 12 | 13 | trait IntegerBits[A <: Data] extends Any with RealBits[A] with Integer[A] {} 14 | 15 | object IntegerBits { 16 | def apply[A <: Data](implicit A: IntegerBits[A]): IntegerBits[A] = A 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/blackbox_compatibility/DspRealVerilatorBB.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chisel3.util.HasBlackBoxResource 7 | 8 | /* 9 | * Uses classname to find verilog implementation of blackbox 10 | */ 11 | trait BlackBoxWithVerilog extends BlackBox with HasBlackBoxResource { 12 | addResource("/" + this.getClass.getSimpleName + ".v") 13 | } 14 | 15 | class BlackboxOneOperand extends BlackBoxWithVerilog { 16 | val io = IO(new Bundle() { 17 | val in = Input(UInt(DspReal.underlyingWidth.W)) 18 | val out = Output(UInt(DspReal.underlyingWidth.W)) 19 | }) 20 | io.suggestName("io") 21 | } 22 | 23 | class BlackboxTwoOperand extends BlackBoxWithVerilog { 24 | val io = IO(new Bundle() { 25 | val in1 = Input(UInt(DspReal.underlyingWidth.W)) 26 | val in2 = Input(UInt(DspReal.underlyingWidth.W)) 27 | val out = Output(UInt(DspReal.underlyingWidth.W)) 28 | }) 29 | io.suggestName("io") 30 | } 31 | 32 | class BlackboxTwoOperandBool extends BlackBoxWithVerilog { 33 | val io = IO(new Bundle() { 34 | val in1 = Input(UInt(DspReal.underlyingWidth.W)) 35 | val in2 = Input(UInt(DspReal.underlyingWidth.W)) 36 | val out = Output(Bool()) 37 | }) 38 | io.suggestName("io") 39 | } 40 | 41 | class BBFAdd extends BlackboxTwoOperand 42 | 43 | class BBFSubtract extends BlackboxTwoOperand 44 | 45 | class BBFMultiply extends BlackboxTwoOperand 46 | 47 | class BBFDivide extends BlackboxTwoOperand 48 | 49 | class BBFGreaterThan extends BlackboxTwoOperandBool 50 | 51 | class BBFGreaterThanEquals extends BlackboxTwoOperandBool 52 | 53 | class BBFLessThan extends BlackboxTwoOperandBool 54 | 55 | class BBFLessThanEquals extends BlackboxTwoOperandBool 56 | 57 | class BBFEquals extends BlackboxTwoOperandBool 58 | 59 | class BBFNotEquals extends BlackboxTwoOperandBool 60 | 61 | /** Math operations from IEEE.1364-2005 * */ 62 | class BBFLn extends BlackboxOneOperand 63 | 64 | class BBFLog10 extends BlackboxOneOperand 65 | 66 | class BBFExp extends BlackboxOneOperand 67 | 68 | class BBFSqrt extends BlackboxOneOperand 69 | 70 | class BBFPow extends BlackboxTwoOperand 71 | 72 | class BBFFloor extends BlackboxOneOperand 73 | 74 | class BBFCeil extends BlackboxOneOperand 75 | 76 | // Not supported by Verilator -- need to build out own approximation 77 | class BBFSin extends BlackboxOneOperand 78 | 79 | class BBFCos extends BlackboxOneOperand 80 | 81 | class BBFTan extends BlackboxOneOperand 82 | 83 | class BBFASin extends BlackboxOneOperand 84 | 85 | class BBFACos extends BlackboxOneOperand 86 | 87 | class BBFATan extends BlackboxOneOperand 88 | 89 | class BBFATan2 extends BlackboxTwoOperand 90 | 91 | class BBFHypot extends BlackboxTwoOperand 92 | 93 | class BBFSinh extends BlackboxOneOperand 94 | 95 | class BBFCosh extends BlackboxOneOperand 96 | 97 | class BBFTanh extends BlackboxOneOperand 98 | 99 | class BBFASinh extends BlackboxOneOperand 100 | 101 | class BBFACosh extends BlackboxOneOperand 102 | 103 | class BBFATanh extends BlackboxOneOperand 104 | 105 | class BBFFromInt extends BlackboxOneOperand 106 | 107 | class BBFToInt extends BlackboxOneOperand 108 | 109 | // Not used 110 | //class BBFIntPart extends BlackboxOneOperand { addVerilog() } 111 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/blackbox_compatibility/TrigUtility.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | object TrigUtility { 6 | 7 | // Sets how accurate things can be 8 | val numTaylorTerms = 8 9 | val atanM = 3 10 | // Double calcs have difficulty with small numbers 11 | val err = 1e6 12 | 13 | // Calculates Bernoulli numbers via the Akiyama–Tanigawa algorithm 14 | // @ https://en.wikipedia.org/wiki/Bernoulli_number 15 | def bernoulli(n: Int): Double = { 16 | this.synchronized { 17 | var temp: Array[Double] = Array.fill(n + 1)(0.0) 18 | for (m <- 0 to n) { 19 | temp(m) = 1.toDouble / (m + 1) 20 | for (j <- m to 1 by -1) { 21 | temp(j - 1) = j * (temp(j - 1) - temp(j)) 22 | } 23 | } 24 | // Bn 25 | temp(0) 26 | } 27 | } 28 | 29 | def factorial(n: Int): Int = (1 to n).product 30 | 31 | def combination(n: Int, k: Int): Double = factorial(n).toDouble / factorial(k) / factorial(n - k) 32 | 33 | // See Taylor series for trig functions @ https://en.wikipedia.org/wiki/Taylor_series 34 | def sinCoeff(nmax: Int): Seq[(Double, Double)] = { 35 | (0 to nmax).map { n => 36 | { 37 | val fact = factorial(2 * n + 1) 38 | val factOutOfBounds = fact / err 39 | // If you divide by too large of a number, things go crazy 40 | val scaleFactor = if (factOutOfBounds <= 1) 1.0 else fact.toDouble / err 41 | val denom = if (factOutOfBounds <= 1) fact else err 42 | (math.pow(-1, n) / denom, scaleFactor) 43 | } 44 | } 45 | } 46 | def cosCoeff(nmax: Int): Seq[Double] = { 47 | (0 to nmax).map(n => math.pow(-1, n) / factorial(2 * n)) 48 | } 49 | def tanCoeff(nmax: Int): Seq[Double] = { 50 | (1 to nmax).map(n => 51 | bernoulli(2 * n) * math.pow(2, 2 * n) * (math.pow(2, 2 * n) - 1) * math.pow(-1, n - 1) / factorial(2 * n) 52 | ) 53 | } 54 | 55 | // Fast convergence of arctan (arcsin, arccos derived) 56 | // See: http://myweb.lmu.edu/hmedina/papers/reprintmonthly156-161-medina.pdf 57 | def a(j: Int, m: Int): Double = { 58 | // Is Even 59 | if (j % 2 == 0) { 60 | val i = j / 2 61 | def sumTerm(k: Int) = math.pow(-1, k) * combination(4 * m, 2 * k) 62 | math.pow(-1, i + 1) * ((i + 1) to (2 * m)).map(k => sumTerm(k)).sum 63 | } else { 64 | val i = (j + 1) / 2 65 | def sumTerm(k: Int) = math.pow(-1, k) * combination(4 * m, 2 * k + 1) 66 | math.pow(-1, i + 1) * (i to (2 * m - 1)).map(k => sumTerm(k)).sum 67 | } 68 | } 69 | 70 | def atanCoeff1(m: Int): Seq[Double] = { 71 | (1 to (2 * m)).map(j => math.pow(-1, j + 1) / (2 * j - 1)) 72 | } 73 | def atanCoeff2(m: Int): Seq[Double] = { 74 | (0 to (4 * m - 2)).map(j => a(j, m) / math.pow(-1, m + 1) / math.pow(4, m) / (4 * m + j + 1)) 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_concrete/DspComplex.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import dsptools.DspException 8 | import breeze.math.Complex 9 | import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor 10 | 11 | import scala.reflect.ClassTag 12 | 13 | object DspComplex { 14 | 15 | def apply[T <: Data: Ring](gen: T): DspComplex[T] = { 16 | if (gen.isLit) throw DspException("Cannot use Lit in single argument DspComplex.apply") 17 | apply(gen.cloneType, gen.cloneType) 18 | } 19 | 20 | // If real, imag are literals, the literals are carried through 21 | // In reality, real and imag should have the same type, so should be using single argument 22 | // apply if you aren't trying t create a Lit 23 | def apply[T <: Data: Ring](real: T, imag: T): DspComplex[T] = { 24 | val newReal = if (real.isLit) real.cloneType else real 25 | val newImag = if (imag.isLit) imag.cloneType else imag 26 | if (real.isLit && imag.isLit) { 27 | new DspComplex(newReal, newImag).Lit(_.real -> real, _.imag -> imag) 28 | } else { 29 | new DspComplex(newReal, newImag) 30 | } 31 | } 32 | 33 | // Needed for assigning to results of operations; should not use in user code for making wires 34 | // Assumes real, imag are not literals 35 | def wire[T <: Data: Ring](real: T, imag: T): DspComplex[T] = { 36 | val result = Wire(DspComplex(real.cloneType, imag.cloneType)) 37 | result.real := real 38 | result.imag := imag 39 | result 40 | } 41 | 42 | // Constant j 43 | // TODO(Paul): this call to wire() should be removed when chisel has literal bundles 44 | def j[T <: Data: Ring]: DspComplex[T] = DspComplex(Ring[T].zero, Ring[T].one) 45 | 46 | // Creates a DspComplex literal of type DspComplex[T] from a Breeze Complex 47 | // Note: when T is FixedPoint, the # of fractional bits is determined via DspContext 48 | def apply[T <: Data: Ring: ConvertableTo](c: Complex): DspComplex[T] = { 49 | DspComplex(ConvertableTo[T].fromDouble(c.real), ConvertableTo[T].fromDouble(c.imag)) 50 | } 51 | // Creates a DspComplex literal where real and imaginary parts have type T (and binary point 52 | // determined by binaryPoint of t) 53 | def proto[T <: Data: Ring: ConvertableTo](c: Complex, t: T): DspComplex[T] = { 54 | DspComplex(ConvertableTo[T].fromDouble(c.real, t), ConvertableTo[T].fromDouble(c.imag, t)) 55 | } 56 | // Creates a DspComplex literal where real and imaginary parts have type T (width/binary point 57 | // determined by width/binaryPoint of t) 58 | def protoWithFixedWidth[T <: Data: Ring: ConvertableTo](c: Complex, t: T): DspComplex[T] = { 59 | DspComplex( 60 | ConvertableTo[T].fromDoubleWithFixedWidth(c.real, t), 61 | ConvertableTo[T].fromDoubleWithFixedWidth(c.imag, t) 62 | ) 63 | } 64 | 65 | } 66 | 67 | class DspComplex[T <: Data: Ring](val real: T, val imag: T)(implicit val ct: ClassTag[DspComplex[T]]) 68 | extends Bundle 69 | with ForceElementwiseConnect[DspComplex[T]] { 70 | 71 | // So old DSP code doesn't break 72 | def imaginary(dummy: Int = 0): T = imag 73 | 74 | // Multiply by j 75 | def mulj(dummy: Int = 0): DspComplex[T] = DspComplex.wire(-imag, real) 76 | // Divide by j 77 | def divj(dummy: Int = 0): DspComplex[T] = DspComplex.wire(imag, -real) 78 | // Complex conjugate 79 | def conj(dummy: Int = 0): DspComplex[T] = DspComplex.wire(real, -imag) 80 | // Absolute square (squared norm) = x^2 + y^2 81 | // Uses implicits 82 | def abssq(dummy: Int = 0): T = (real * real) + (imag * imag) 83 | 84 | def underlyingType(dummy: Int = 0): String = { 85 | real match { 86 | case _: FixedPoint => "fixed" 87 | case _: DspReal => "real" 88 | case _: SInt => "SInt" 89 | case _: UInt => "UInt" 90 | case _ => throw DspException(s"DspComplex found unsupported underlying type: ${real.getClass.getName}") 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_concrete/RealTrig.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | // Make using these ops more like using math.opName 6 | object RealTrig { 7 | def ln(x: DspReal) = x.ln 8 | def log10(x: DspReal) = x.log10 9 | def exp(x: DspReal) = x.exp 10 | def sqrt(x: DspReal) = x.sqrt 11 | def pow(x: DspReal, n: DspReal) = x.pow(n) 12 | def sin(x: DspReal) = x.sin 13 | def cos(x: DspReal) = x.cos 14 | def tan(x: DspReal) = x.tan 15 | def atan(x: DspReal) = x.atan 16 | def asin(x: DspReal) = x.asin 17 | def acos(x: DspReal) = x.acos 18 | def atan2(y: DspReal, x: DspReal) = y.atan2(x) 19 | def hypot(x: DspReal, y: DspReal) = x.hypot(y) 20 | def sinh(x: DspReal) = x.sinh 21 | def cosh(x: DspReal) = x.cosh 22 | def tanh(x: DspReal) = x.tanh 23 | def asinh(x: DspReal) = x.asinh 24 | def acosh(x: DspReal) = x.acosh 25 | def atanh(x: DspReal) = x.tanh 26 | } 27 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_types/DspComplexTypeClass.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import dsptools.hasContext 8 | import implicits._ 9 | import chisel3.util.ShiftRegister 10 | import dsptools.DspException 11 | 12 | abstract class DspComplexRing[T <: Data: Ring] extends Ring[DspComplex[T]] with hasContext { 13 | def plus(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 14 | DspComplex.wire(f.real + g.real, f.imag + g.imag) 15 | } 16 | def plusContext(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 17 | DspComplex.wire(f.real.context_+(g.real), f.imag.context_+(g.imag)) 18 | } 19 | 20 | /** 21 | * The builtin times calls +. Ideally we'd like to use growing addition, but we're relying on typeclasses and the 22 | * default + for UInt, SInt, etc. is wrapping. Thus, we're making an escape hatch just for the default (non-context) 23 | * complex multiply. 24 | * @param l 25 | * @param r 26 | * @return the sum of l and r, preferrably growing 27 | */ 28 | protected def plusForTimes(l: T, r: T): T 29 | 30 | def times(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 31 | val c_p_d = g.real + g.imag 32 | val a_p_b = f.real + f.imag 33 | val b_m_a = f.imag - f.real 34 | val ac_p_ad = f.real * c_p_d 35 | val ad_p_bd = a_p_b * g.imag 36 | val bc_m_ac = b_m_a * g.real 37 | DspComplex.wire(ac_p_ad - ad_p_bd, ac_p_ad + bc_m_ac) 38 | } 39 | def timesContext(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 40 | if (context.complexUse4Muls) 41 | DspComplex.wire( 42 | (f.real.context_*(g.real)).context_-(f.imag.context_*(g.imag)), 43 | (f.real.context_*(g.imag)).context_+(f.imag.context_*(g.real)) 44 | ) 45 | else { 46 | val fRealDly = ShiftRegister(f.real, context.numAddPipes) 47 | val gRealDly = ShiftRegister(g.real, context.numAddPipes) 48 | val gImagDly = ShiftRegister(g.imag, context.numAddPipes) 49 | val c_p_d = g.real.context_+(g.imag) 50 | val a_p_b = f.real.context_+(f.imag) 51 | val b_m_a = f.imag.context_-(f.real) 52 | val ac_p_ad = fRealDly.context_*(c_p_d) 53 | val ad_p_bd = a_p_b.context_*(gImagDly) 54 | val bc_m_ac = b_m_a.context_*(gRealDly) 55 | DspComplex.wire(ac_p_ad.context_-(ad_p_bd), ac_p_ad.context_+(bc_m_ac)) 56 | } 57 | } 58 | def one: DspComplex[T] = DspComplex(Ring[T].one, Ring[T].zero) 59 | // Only assigns real part as x 60 | override def fromInt(x: Int): DspComplex[T] = DspComplex(Ring[T].fromInt(x), Ring[T].zero) 61 | def zero: DspComplex[T] = DspComplex(Ring[T].zero, Ring[T].zero) 62 | def negate(f: DspComplex[T]): DspComplex[T] = DspComplex.wire(-f.real, -f.imag) 63 | def negateContext(f: DspComplex[T]): DspComplex[T] = { 64 | DspComplex.wire(f.real.context_unary_-, f.imag.context_unary_-) 65 | } 66 | override def minus(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 67 | DspComplex.wire(f.real - g.real, f.imag - g.imag) 68 | } 69 | def minusContext(f: DspComplex[T], g: DspComplex[T]): DspComplex[T] = { 70 | DspComplex.wire(f.real.context_-(g.real), f.imag.context_-(g.imag)) 71 | } 72 | } 73 | 74 | class DspComplexRingUInt extends DspComplexRing[UInt] { 75 | override def plusForTimes(l: UInt, r: UInt): UInt = l +& r 76 | } 77 | 78 | class DspComplexRingSInt extends DspComplexRing[SInt] { 79 | override def plusForTimes(l: SInt, r: SInt): SInt = l +& r 80 | } 81 | 82 | class DspComplexRingFixed extends DspComplexRing[FixedPoint] { 83 | override def plusForTimes(l: FixedPoint, r: FixedPoint): FixedPoint = l +& r 84 | } 85 | 86 | class DspComplexRingData[T <: Data: Ring] extends DspComplexRing[T] { 87 | override protected def plusForTimes(l: T, r: T): T = l + r 88 | } 89 | 90 | class DspComplexEq[T <: Data: Eq] extends Eq[DspComplex[T]] with hasContext { 91 | override def eqv(x: DspComplex[T], y: DspComplex[T]): Bool = { 92 | Eq[T].eqv(x.real, y.real) && Eq[T].eqv(x.imag, y.imag) 93 | } 94 | override def neqv(x: DspComplex[T], y: DspComplex[T]): Bool = { 95 | Eq[T].neqv(x.real, y.real) || Eq[T].neqv(x.imag, y.imag) 96 | } 97 | } 98 | 99 | class DspComplexBinaryRepresentation[T <: Data: Ring: BinaryRepresentation] 100 | extends BinaryRepresentation[DspComplex[T]] 101 | with hasContext { 102 | override def shl(a: DspComplex[T], n: Int): DspComplex[T] = throw DspException("Can't shl on complex") 103 | override def shl(a: DspComplex[T], n: UInt): DspComplex[T] = throw DspException("Can't shl on complex") 104 | override def shr(a: DspComplex[T], n: Int): DspComplex[T] = throw DspException("Can't shr on complex") 105 | override def shr(a: DspComplex[T], n: UInt): DspComplex[T] = throw DspException("Can't shr on complex") 106 | override def div2(a: DspComplex[T], n: Int): DspComplex[T] = DspComplex.wire(a.real.div2(n), a.imag.div2(n)) 107 | override def mul2(a: DspComplex[T], n: Int): DspComplex[T] = DspComplex.wire(a.real.mul2(n), a.imag.mul2(n)) 108 | def clip(a: DspComplex[T], b: DspComplex[T]): DspComplex[T] = throw DspException("Can't clip on complex") 109 | def signBit(a: DspComplex[T]): Bool = throw DspException("Can't get sign bit on complex") 110 | def trimBinary(a: DspComplex[T], n: Option[Int]): DspComplex[T] = 111 | DspComplex.wire(BinaryRepresentation[T].trimBinary(a.real, n), BinaryRepresentation[T].trimBinary(a.imag, n)) 112 | } 113 | 114 | trait GenericDspComplexImpl { 115 | implicit def DspComplexRingDataImpl[T <: Data: Ring] = new DspComplexRingData[T]() 116 | implicit def DspComplexEq[T <: Data: Eq] = new DspComplexEq[T]() 117 | implicit def DspComplexBinaryRepresentation[T <: Data: Ring: BinaryRepresentation] = 118 | new DspComplexBinaryRepresentation[T]() 119 | } 120 | 121 | trait DspComplexImpl extends GenericDspComplexImpl { 122 | implicit def DspComplexRingUIntImpl = new DspComplexRingUInt 123 | implicit def DspComplexRingSIntImpl = new DspComplexRingSInt 124 | implicit def DspComplexRingFixedImpl = new DspComplexRingFixed 125 | } 126 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_types/DspRealTypeClass.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.util.ShiftRegister 6 | import chisel3._ 7 | import dsptools.{hasContext, DspContext, NoTrim} 8 | import fixedpoint._ 9 | 10 | import scala.language.implicitConversions 11 | 12 | trait DspRealRing extends Any with Ring[DspReal] with hasContext { 13 | def one: DspReal = DspReal(1.0) 14 | def zero: DspReal = DspReal(0.0) 15 | def plus(f: DspReal, g: DspReal): DspReal = f + g 16 | def plusContext(f: DspReal, g: DspReal): DspReal = { 17 | ShiftRegister(f + g, context.numAddPipes) 18 | } 19 | override def minus(f: DspReal, g: DspReal): DspReal = f - g 20 | def minusContext(f: DspReal, g: DspReal): DspReal = { 21 | ShiftRegister(f - g, context.numAddPipes) 22 | } 23 | def negate(f: DspReal): DspReal = minus(zero, f) 24 | def negateContext(f: DspReal): DspReal = minusContext(zero, f) 25 | def times(f: DspReal, g: DspReal): DspReal = f * g 26 | def timesContext(f: DspReal, g: DspReal): DspReal = { 27 | ShiftRegister(f * g, context.numMulPipes) 28 | } 29 | } 30 | 31 | trait DspRealOrder extends Any with Order[DspReal] with hasContext { 32 | override def compare(x: DspReal, y: DspReal): ComparisonBundle = { 33 | ComparisonHelper(x === y, x < y) 34 | } 35 | override def eqv(x: DspReal, y: DspReal): Bool = x === y 36 | override def neqv(x: DspReal, y: DspReal): Bool = x != y 37 | override def lt(x: DspReal, y: DspReal): Bool = x < y 38 | override def lteqv(x: DspReal, y: DspReal): Bool = x <= y 39 | override def gt(x: DspReal, y: DspReal): Bool = x > y 40 | override def gteqv(x: DspReal, y: DspReal): Bool = x >= y 41 | // min, max depends on lt, gt & mux 42 | } 43 | 44 | trait DspRealSigned extends Any with Signed[DspReal] with DspRealRing with hasContext { 45 | def signum(a: DspReal): ComparisonBundle = { 46 | ComparisonHelper(a === DspReal(0.0), a < DspReal(0.0)) 47 | } 48 | def abs(a: DspReal): DspReal = a.abs 49 | def context_abs(a: DspReal): DspReal = { 50 | Mux( 51 | isSignNonNegative(ShiftRegister(a, context.numAddPipes)), 52 | ShiftRegister(a, context.numAddPipes), 53 | super[DspRealRing].minusContext(DspReal(0.0), a) 54 | ) 55 | } 56 | 57 | override def isSignZero(a: DspReal): Bool = a === DspReal(0.0) 58 | override def isSignNegative(a: DspReal): Bool = a < DspReal(0.0) 59 | // isSignPositive, isSignNonZero, isSignNonPositive, isSignNonNegative derived from above (!) 60 | } 61 | 62 | trait DspRealIsReal extends Any with IsReal[DspReal] with DspRealOrder with DspRealSigned with hasContext { 63 | def ceil(a: DspReal): DspReal = { 64 | a.ceil 65 | } 66 | def context_ceil(a: DspReal): DspReal = { 67 | ShiftRegister(a, context.numAddPipes).ceil 68 | } 69 | def floor(a: DspReal): DspReal = a.floor 70 | def isWhole(a: DspReal): Bool = a === round(a) 71 | // Round *half up* -- Different from System Verilog definition! (where half is rounded away from zero) 72 | // according to 5.7.2 (http://www.ece.uah.edu/~gaede/cpe526/2012%20System%20Verilog%20Language%20Reference%20Manual.pdf) 73 | def round(a: DspReal): DspReal = a.round 74 | def truncate(a: DspReal): DspReal = { 75 | Mux( 76 | ShiftRegister(a, context.numAddPipes) < DspReal(0.0), 77 | context_ceil(a), 78 | floor(ShiftRegister(a, context.numAddPipes)) 79 | ) 80 | } 81 | } 82 | 83 | trait ConvertableToDspReal extends ConvertableTo[DspReal] with hasContext { 84 | def fromShort(n: Short): DspReal = fromInt(n.toInt) 85 | def fromByte(n: Byte): DspReal = fromInt(n.toInt) 86 | def fromInt(n: Int): DspReal = fromBigInt(BigInt(n)) 87 | def fromFloat(n: Float): DspReal = fromDouble(n.toDouble) 88 | def fromBigDecimal(n: BigDecimal): DspReal = fromDouble(n.doubleValue) 89 | def fromLong(n: Long): DspReal = fromBigInt(BigInt(n)) 90 | def fromType[B](n: B)(implicit c: ConvertableFrom[B]): DspReal = fromDouble(c.toDouble(n)) 91 | def fromBigInt(n: BigInt): DspReal = DspReal(n.doubleValue) 92 | def fromDouble(n: Double): DspReal = DspReal(n) 93 | override def fromDouble(d: Double, a: DspReal): DspReal = fromDouble(d) 94 | // Ignores width 95 | override def fromDoubleWithFixedWidth(d: Double, a: DspReal): DspReal = fromDouble(d) 96 | } 97 | 98 | trait ConvertableFromDspReal extends ChiselConvertableFrom[DspReal] with hasContext { 99 | // intPart requires truncate, asFixed requires round 100 | def asReal(a: DspReal): DspReal = a 101 | } 102 | 103 | trait BinaryRepresentationDspReal extends BinaryRepresentation[DspReal] with hasContext { 104 | def shl(a: DspReal, n: Int): DspReal = a * DspReal(math.pow(2, n)) 105 | def shl(a: DspReal, n: UInt): DspReal = { 106 | require(n.widthKnown, "n Width must be known for shl with DspReal") 107 | val max = (1 << n.getWidth) - 1 108 | val lut = VecInit((0 to max).map(x => DspReal(math.pow(2, x)))) 109 | a * lut(n) 110 | } 111 | def shr(a: DspReal, n: Int): DspReal = div2(a, n) 112 | def shr(a: DspReal, n: UInt): DspReal = { 113 | require(n.widthKnown, "n Width must be known for shr with DspReal") 114 | val max = (1 << n.getWidth) - 1 115 | val lut = VecInit((0 to max).map(x => DspReal(math.pow(2.0, -x)))) 116 | a * lut(n) 117 | } 118 | 119 | def clip(a: DspReal, b: DspReal): DspReal = a 120 | 121 | // mul2 consistent with shl 122 | // signBit relies on Signed 123 | override def div2(a: DspReal, n: Int): DspReal = a / DspReal(math.pow(2, n)) 124 | // Used purely for fixed point precision adjustment -- just passes DspReal through 125 | def trimBinary(a: DspReal, n: Option[Int]): DspReal = a 126 | } 127 | 128 | trait DspRealReal 129 | extends DspRealRing 130 | with DspRealIsReal 131 | with ConvertableToDspReal 132 | with ConvertableFromDspReal 133 | with BinaryRepresentationDspReal 134 | with RealBits[DspReal] 135 | with hasContext { 136 | def signBit(a: DspReal): Bool = isSignNegative(a) 137 | override def fromInt(n: Int): DspReal = super[ConvertableToDspReal].fromInt(n) 138 | override def fromBigInt(n: BigInt): DspReal = super[ConvertableToDspReal].fromBigInt(n) 139 | def intPart(a: DspReal): SInt = truncate(a).toSInt 140 | // WARNING: Beware of overflow(!) 141 | def asFixed(a: DspReal, proto: FixedPoint): FixedPoint = { 142 | require(proto.binaryPoint.known, "Binary point must be known for DspReal -> FixedPoint") 143 | val bp = proto.binaryPoint.get 144 | // WARNING: Round half up! 145 | val out = Wire(proto.cloneType) 146 | out := DspContext.withTrimType(NoTrim) { 147 | // round is round half up 148 | round(a * DspReal((1 << bp).toDouble)).toSInt.asFixed.div2(bp) 149 | } 150 | out 151 | } 152 | } 153 | 154 | trait DspRealImpl { 155 | implicit object DspRealRealImpl extends DspRealReal 156 | } 157 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_types/SIntTypeClass.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chisel3.util.{Cat, ShiftRegister} 7 | import dsptools.{hasContext, DspContext, DspException, Grow, NoTrim, Saturate, Wrap} 8 | import fixedpoint._ 9 | 10 | import scala.language.implicitConversions 11 | 12 | /** 13 | * Defines basic math functions for SInt 14 | */ 15 | trait SIntRing extends Any with Ring[SInt] with hasContext { 16 | def zero: SInt = 0.S 17 | def one: SInt = 1.S 18 | def plus(f: SInt, g: SInt): SInt = f + g 19 | def plusContext(f: SInt, g: SInt): SInt = { 20 | // TODO: Saturating mux should be outside of ShiftRegister 21 | val sum = context.overflowType match { 22 | case Grow => f +& g 23 | case Wrap => f +% g 24 | case _ => throw DspException("Saturating add hasn't been implemented") 25 | } 26 | ShiftRegister(sum, context.numAddPipes) 27 | } 28 | override def minus(f: SInt, g: SInt): SInt = f - g 29 | def minusContext(f: SInt, g: SInt): SInt = { 30 | val diff = context.overflowType match { 31 | case Grow => f -& g 32 | case Wrap => f -% g 33 | case _ => throw DspException("Saturating subtractor hasn't been implemented") 34 | } 35 | ShiftRegister(diff, context.numAddPipes) 36 | } 37 | def negate(f: SInt): SInt = { 38 | -f 39 | } 40 | def negateContext(f: SInt): SInt = { 41 | //TODO: should this be minusContext, had been just minus 42 | minusContext(0.S, f) 43 | } 44 | def times(f: SInt, g: SInt): SInt = f * g 45 | def timesContext(f: SInt, g: SInt): SInt = { 46 | // TODO: Overflow via ranging in FIRRTL? 47 | ShiftRegister(f * g, context.numMulPipes) 48 | } 49 | } 50 | 51 | trait SIntOrder extends Any with Order[SInt] with hasContext { 52 | override def compare(x: SInt, y: SInt): ComparisonBundle = { 53 | ComparisonHelper(x === y, x < y) 54 | } 55 | override def eqv(x: SInt, y: SInt): Bool = x === y 56 | override def neqv(x: SInt, y: SInt): Bool = x =/= y 57 | override def lt(x: SInt, y: SInt): Bool = x < y 58 | override def lteqv(x: SInt, y: SInt): Bool = x <= y 59 | override def gt(x: SInt, y: SInt): Bool = x > y 60 | override def gteqv(x: SInt, y: SInt): Bool = x >= y 61 | // min, max depends on lt, gt & mux 62 | } 63 | 64 | trait SIntSigned extends Any with Signed[SInt] with hasContext { 65 | def signum(a: SInt): ComparisonBundle = { 66 | ComparisonHelper(a === 0.S, a < 0.S) 67 | } 68 | override def isSignZero(a: SInt): Bool = a === 0.S 69 | override def isSignNegative(a: SInt): Bool = { 70 | if (a.widthKnown) a(a.getWidth - 1) 71 | else a < 0.S 72 | } 73 | // isSignPositive, isSignNonZero, isSignNonPositive, isSignNonNegative derived from above (!) 74 | // abs requires ring (for overflow) so overridden later 75 | // context_abs requires ring (for overflow) so overridden later 76 | } 77 | 78 | trait SIntIsReal extends Any with IsIntegral[SInt] with SIntOrder with SIntSigned with hasContext { 79 | // In IsIntegral: ceil, floor, round, truncate (from IsReal) already defined as itself; 80 | // isWhole always true 81 | // -5, -3, -1, 1, 3, 5, etc. 82 | def isOdd(a: SInt): Bool = a(0) 83 | // isEven derived from isOdd 84 | // Note: whatever Chisel does -- double check it's what you expect when using it 85 | // Generally better to use your own mod if you know input bounds 86 | def mod(a: SInt, b: SInt): SInt = a % b 87 | } 88 | 89 | trait ConvertableToSInt extends ConvertableTo[SInt] with hasContext { 90 | // Note: Double converted to Int via round first! 91 | def fromShort(n: Short): SInt = fromInt(n.toInt) 92 | def fromByte(n: Byte): SInt = fromInt(n.toInt) 93 | def fromInt(n: Int): SInt = fromBigInt(BigInt(n)) 94 | def fromFloat(n: Float): SInt = fromDouble(n.toDouble) 95 | def fromBigDecimal(n: BigDecimal): SInt = fromDouble(n.doubleValue) 96 | def fromLong(n: Long): SInt = fromBigInt(BigInt(n)) 97 | def fromType[B](n: B)(implicit c: ConvertableFrom[B]): SInt = fromBigInt(c.toBigInt(n)) 98 | def fromBigInt(n: BigInt): SInt = n.S 99 | def fromDouble(n: Double): SInt = n.round.toInt.S 100 | // Second argument needed for fixed pt binary point (unused here) 101 | override def fromDouble(d: Double, a: SInt): SInt = fromDouble(d) 102 | override def fromDoubleWithFixedWidth(d: Double, a: SInt): SInt = { 103 | require(a.widthKnown, "SInt width not known!") 104 | val intVal = d.round.toInt 105 | val intBits = BigInt(intVal).bitLength + 1 106 | require(intBits <= a.getWidth, "Lit can't fit in prototype SInt bitwidth") 107 | intVal.asSInt(a.getWidth.W) 108 | } 109 | } 110 | 111 | trait ConvertableFromSInt extends ChiselConvertableFrom[SInt] with hasContext { 112 | def intPart(a: SInt): SInt = a 113 | 114 | // Converts to FixedPoint with 0 fractional bits (Note: proto only used for real) 115 | override def asFixed(a: SInt): FixedPoint = a.asFixedPoint(0.BP) 116 | def asFixed(a: SInt, proto: FixedPoint): FixedPoint = asFixed(a) 117 | // Converts to (signed) DspReal 118 | def asReal(a: SInt): DspReal = DspReal(a) 119 | } 120 | 121 | trait BinaryRepresentationSInt extends BinaryRepresentation[SInt] with hasContext { 122 | def clip(a: SInt, b: SInt): SInt = ??? 123 | def shl(a: SInt, n: Int): SInt = a << n 124 | def shl(a: SInt, n: UInt): SInt = a << n 125 | // Note: This rounds to negative infinity (smallest abs. value for negative #'s is -1) 126 | def shr(a: SInt, n: Int): SInt = a >> n 127 | def shr(a: SInt, n: UInt): SInt = a >> n 128 | // Doesn't affect anything except FixedPoint (no such thing as negative n) 129 | override def trimBinary(a: SInt, n: Int): SInt = a 130 | def trimBinary(a: SInt, n: Option[Int]): SInt = a 131 | // mul2 consistent with shl 132 | // signBit relies on Signed, div2 relies on ChiselConvertableFrom 133 | } 134 | 135 | trait SIntInteger 136 | extends SIntRing 137 | with SIntIsReal 138 | with ConvertableToSInt 139 | with ConvertableFromSInt 140 | with BinaryRepresentationSInt 141 | with IntegerBits[SInt] 142 | with hasContext { 143 | def signBit(a: SInt): Bool = isSignNegative(a) 144 | // fromSInt also included in Ring 145 | override def fromInt(n: Int): SInt = super[ConvertableToSInt].fromInt(n) 146 | override def fromBigInt(n: BigInt): SInt = super[ConvertableToSInt].fromBigInt(n) 147 | // Overflow only on most negative 148 | def abs(a: SInt): SInt = Mux(isSignNegative(a), super[SIntRing].minus(0.S, a), a) 149 | //scalastyle:off method.name 150 | def context_abs(a: SInt): SInt = { 151 | Mux( 152 | ShiftRegister(a, context.numAddPipes) >= 0.S, 153 | ShiftRegister(a, context.numAddPipes), 154 | super[SIntRing].minusContext(0.S, a) 155 | ) 156 | } 157 | 158 | // Rounds result to nearest int (half up) for more math-y division 159 | override def div2(a: SInt, n: Int): SInt = a.widthOption match { 160 | // If shifting more than width, guaranteed to be closer to 0 161 | case Some(w) if n > w => 0.S 162 | // TODO: Is this too conservative? 163 | case _ => { 164 | val div2Out = DspContext.withTrimType(NoTrim) { asFixed(a).div2(n) } 165 | div2Out.trimBinary(0).asSInt 166 | } 167 | } 168 | } 169 | 170 | trait SIntImpl { 171 | implicit object SIntIntegerImpl extends SIntInteger 172 | } 173 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/chisel_types/UIntTypeClass.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chisel3.util.{Cat, ShiftRegister} 7 | import dsptools.{hasContext, DspContext, DspException, Grow, Saturate, Wrap} 8 | import fixedpoint._ 9 | 10 | import scala.language.implicitConversions 11 | 12 | /** 13 | * Defines basic math functions for UInt 14 | */ 15 | trait UIntRing extends Any with Ring[UInt] with hasContext { 16 | def zero: UInt = 0.U 17 | def one: UInt = 1.U 18 | def plus(f: UInt, g: UInt): UInt = f + g 19 | def plusContext(f: UInt, g: UInt): UInt = { 20 | // TODO: Saturating mux should be outside of ShiftRegister 21 | val sum = context.overflowType match { 22 | case Grow => f +& g 23 | case Wrap => f +% g 24 | case _ => throw DspException("Saturating add hasn't been implemented") 25 | } 26 | ShiftRegister(sum, context.numAddPipes) 27 | } 28 | override def minus(f: UInt, g: UInt): UInt = f - g 29 | def minusContext(f: UInt, g: UInt): UInt = { 30 | val diff = context.overflowType match { 31 | case Grow => throw DspException("OverflowType Grow is not supported for UInt subtraction") 32 | case Wrap => f -% g 33 | case _ => throw DspException("Saturating subtractor hasn't been implemented") 34 | } 35 | ShiftRegister(diff.asUInt, context.numAddPipes) 36 | } 37 | def negate(f: UInt): UInt = -f 38 | def negateContext(f: UInt): UInt = throw DspException("Can't negate UInt and get UInt") 39 | def times(f: UInt, g: UInt): UInt = f * g 40 | def timesContext(f: UInt, g: UInt): UInt = { 41 | // TODO: Overflow via ranging in FIRRTL? 42 | ShiftRegister(f * g, context.numMulPipes) 43 | } 44 | } 45 | 46 | trait UIntOrder extends Any with Order[UInt] with hasContext { 47 | override def compare(x: UInt, y: UInt): ComparisonBundle = { 48 | ComparisonHelper(x === y, x < y) 49 | } 50 | override def eqv(x: UInt, y: UInt): Bool = x === y 51 | override def neqv(x: UInt, y: UInt): Bool = x =/= y 52 | override def lt(x: UInt, y: UInt): Bool = x < y 53 | override def lteqv(x: UInt, y: UInt): Bool = x <= y 54 | override def gt(x: UInt, y: UInt): Bool = x > y 55 | override def gteqv(x: UInt, y: UInt): Bool = x >= y 56 | // min, max depends on lt, gt 57 | } 58 | 59 | trait UIntSigned extends Any with Signed[UInt] with hasContext { 60 | def signum(a: UInt): ComparisonBundle = { 61 | ComparisonHelper(a === 0.U, a < 0.U) 62 | } 63 | def abs(a: UInt): UInt = a // UInts are unsigned! 64 | def context_abs(a: UInt): UInt = a // UInts are unsigned! 65 | override def isSignZero(a: UInt): Bool = a === 0.U 66 | override def isSignPositive(a: UInt): Bool = !isSignZero(a) 67 | override def isSignNegative(a: UInt): Bool = false.B 68 | // isSignNonZero, isSignNonPositive, isSignNonNegative derived from above (!) 69 | } 70 | 71 | trait UIntIsReal extends Any with IsIntegral[UInt] with UIntOrder with UIntSigned with hasContext { 72 | // In IsIntegral: ceil, floor, round, truncate (from IsReal) already defined as itself; 73 | // isWhole always true 74 | 75 | // Unsure what happens if you have a zero-width wire 76 | def isOdd(a: UInt): Bool = a(0) 77 | // isEven derived from isOdd 78 | // Note: whatever Chisel does 79 | // Generally better to use your own mod if you know input bounds 80 | def mod(a: UInt, b: UInt): UInt = a % b 81 | } 82 | 83 | trait ConvertableToUInt extends ConvertableTo[UInt] with hasContext { 84 | // Note: Double converted to Int via round first! 85 | def fromShort(n: Short): UInt = fromInt(n.toInt) 86 | def fromByte(n: Byte): UInt = fromInt(n.toInt) 87 | def fromInt(n: Int): UInt = fromBigInt(BigInt(n)) 88 | def fromFloat(n: Float): UInt = fromDouble(n.toDouble) 89 | def fromBigDecimal(n: BigDecimal): UInt = fromDouble(n.doubleValue) 90 | def fromLong(n: Long): UInt = fromBigInt(BigInt(n)) 91 | def fromType[B](n: B)(implicit c: ConvertableFrom[B]): UInt = fromBigInt(c.toBigInt(n)) 92 | def fromBigInt(n: BigInt): UInt = { 93 | require(n >= 0, "Literal to UInt needs to be >= 0") 94 | n.U 95 | } 96 | def fromDouble(n: Double): UInt = { 97 | require(n >= 0, "Double literal to UInt needs to be >= 0") 98 | n.round.toInt.U 99 | } 100 | // Second argument needed for fixed pt binary point (unused here) 101 | override def fromDouble(d: Double, a: UInt): UInt = fromDouble(d) 102 | override def fromDoubleWithFixedWidth(d: Double, a: UInt): UInt = { 103 | require(a.widthKnown, "UInt width not known!") 104 | require(d >= 0, "Double literal to UInt needs to be >= 0") 105 | val intVal = d.round.toInt 106 | val intBits = BigInt(intVal).bitLength 107 | require(intBits <= a.getWidth, "Lit can't fit in prototype UInt bitwidth") 108 | intVal.asUInt(a.getWidth.W) 109 | } 110 | } 111 | 112 | trait ConvertableFromUInt extends ChiselConvertableFrom[UInt] with hasContext { 113 | // Bit grow by 1, but always positive to maintain correctness 114 | def intPart(a: UInt): SInt = Cat(false.B, a).asSInt 115 | 116 | // Converts to FixedPoint with 0 fractional bits (second arg only used for DspReal) 117 | override def asFixed(a: UInt): FixedPoint = intPart(a).asFixedPoint(0.BP) 118 | def asFixed(a: UInt, proto: FixedPoint): FixedPoint = asFixed(a) 119 | // Converts to (signed) DspReal 120 | def asReal(a: UInt): DspReal = DspReal(intPart(a)) 121 | } 122 | 123 | trait BinaryRepresentationUInt extends BinaryRepresentation[UInt] with hasContext { 124 | def shl(a: UInt, n: Int): UInt = a << n 125 | def shl(a: UInt, n: UInt): UInt = a << n 126 | def shr(a: UInt, n: Int): UInt = a >> n 127 | def shr(a: UInt, n: UInt): UInt = a >> n 128 | def clip(a: UInt, n: UInt): UInt = ??? 129 | // Ignores negative trims (n not used for anything except Fixed) 130 | override def trimBinary(a: UInt, n: Int): UInt = a 131 | def trimBinary(a: UInt, n: Option[Int]): UInt = a 132 | // signBit relies on Signed 133 | // mul2, div2 consistent with shl, shr 134 | } 135 | 136 | trait UIntInteger 137 | extends UIntRing 138 | with UIntIsReal 139 | with ConvertableToUInt 140 | with ConvertableFromUInt 141 | with BinaryRepresentationUInt 142 | with IntegerBits[UInt] 143 | with hasContext { 144 | def signBit(a: UInt): Bool = isSignNegative(a) 145 | // fromUInt also included in Ring 146 | override def fromInt(n: Int): UInt = super[ConvertableToUInt].fromInt(n) 147 | override def fromBigInt(n: BigInt): UInt = super[ConvertableToUInt].fromBigInt(n) 148 | } 149 | 150 | trait UIntImpl { 151 | implicit object UIntIntegerImpl extends UIntInteger 152 | } 153 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/convertible_types/ChiselConvertableFrom.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import fixedpoint._ 6 | import chisel3.{Data, SInt} 7 | import dsptools.DspException 8 | 9 | object ChiselConvertableFrom { 10 | def apply[A <: Data](implicit A: ChiselConvertableFrom[A]): ChiselConvertableFrom[A] = A 11 | } 12 | 13 | trait ChiselConvertableFrom[A <: Data] extends Any { 14 | def intPart(a: A): SInt 15 | 16 | def asFixed(a: A, proto: FixedPoint): FixedPoint 17 | def asFixed(a: A): FixedPoint = throw DspException("As fixed needs prototype argument!") 18 | 19 | def asReal(a: A): DspReal 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/convertible_types/ConvertableTo.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.Data 6 | 7 | object ConvertableTo { 8 | def apply[A <: Data](implicit A: ConvertableTo[A]): ConvertableTo[A] = A 9 | } 10 | 11 | trait ConvertableTo[A <: Data] extends Any with spire.math.ConvertableTo[A] { 12 | def fromDouble(d: Double, a: A): A 13 | def fromDoubleWithFixedWidth(d: Double, a: A): A 14 | } 15 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/implicits/AllOps.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import spire.macros.Ops 7 | 8 | import scala.language.experimental.macros 9 | import fixedpoint._ 10 | 11 | /** 12 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 13 | * Java Bools. I suppose a more general solution would be generic in 14 | * return type, but the use cases there seem obscure. 15 | */ 16 | 17 | final class EqOps[A <: Data](lhs: A)(implicit ev: Eq[A]) { 18 | def ===(rhs: A): Bool = macro Ops.binop[A, Bool] 19 | def =!=(rhs: A): Bool = macro Ops.binop[A, Bool] 20 | // Consistency with Chisel 21 | def =/=(rhs: A): Bool = ev.eqv(lhs, rhs) 22 | } 23 | 24 | final class PartialOrderOps[A <: Data](lhs: A)(implicit ev: PartialOrder[A]) { 25 | def >(rhs: A): Bool = macro Ops.binop[A, Bool] 26 | def >=(rhs: A): Bool = macro Ops.binop[A, Bool] 27 | def <(rhs: A): Bool = macro Ops.binop[A, Bool] 28 | def <=(rhs: A): Bool = macro Ops.binop[A, Bool] 29 | 30 | def partialCompare(rhs: A): Double = macro Ops.binop[A, Double] 31 | def tryCompare(rhs: A): Option[Int] = macro Ops.binop[A, Option[Int]] 32 | def pmin(rhs: A): Option[A] = macro Ops.binop[A, A] 33 | def pmax(rhs: A): Option[A] = macro Ops.binop[A, A] 34 | 35 | def >(rhs: Int)(implicit ev1: Ring[A]): Bool = macro Ops.binopWithLift[Int, Ring[A], A] 36 | def >=(rhs: Int)(implicit ev1: Ring[A]): Bool = macro Ops.binopWithLift[Int, Ring[A], A] 37 | def <(rhs: Int)(implicit ev1: Ring[A]): Bool = macro Ops.binopWithLift[Int, Ring[A], A] 38 | def <=(rhs: Int)(implicit ev1: Ring[A]): Bool = macro Ops.binopWithLift[Int, Ring[A], A] 39 | 40 | def >(rhs: Double)(implicit ev1: Field[A]): Bool = macro Ops.binopWithLift[Int, Field[A], A] 41 | def >=(rhs: Double)(implicit ev1: Field[A]): Bool = macro Ops.binopWithLift[Int, Field[A], A] 42 | def <(rhs: Double)(implicit ev1: Field[A]): Bool = macro Ops.binopWithLift[Int, Field[A], A] 43 | def <=(rhs: Double)(implicit ev1: Field[A]): Bool = macro Ops.binopWithLift[Int, Field[A], A] 44 | 45 | def >(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Bool = (c.toNumber(lhs) > rhs).B 46 | def >=(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Bool = (c.toNumber(lhs) >= rhs).B 47 | def <(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Bool = (c.toNumber(lhs) < rhs).B 48 | def <=(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Bool = (c.toNumber(lhs) <= rhs).B 49 | } 50 | 51 | final class OrderOps[A <: Data](lhs: A)(implicit ev: Order[A]) { 52 | def compare(rhs: A): ComparisonBundle = macro Ops.binop[A, ComparisonBundle] 53 | def min(rhs: A): A = macro Ops.binop[A, A] 54 | def max(rhs: A): A = macro Ops.binop[A, A] 55 | 56 | def compare(rhs: Int)(implicit ev1: Ring[A]): Int = macro Ops.binopWithLift[Int, Ring[A], A] 57 | def min(rhs: Int)(implicit ev1: Ring[A]): A = macro Ops.binopWithLift[Int, Ring[A], A] 58 | def max(rhs: Int)(implicit ev1: Ring[A]): A = macro Ops.binopWithLift[Int, Ring[A], A] 59 | 60 | def compare(rhs: Double)(implicit ev1: Field[A]): Int = macro Ops.binopWithLift[Int, Field[A], A] 61 | def min(rhs: Double)(implicit ev1: Field[A]): A = macro Ops.binopWithLift[Int, Field[A], A] 62 | def max(rhs: Double)(implicit ev1: Field[A]): A = macro Ops.binopWithLift[Int, Field[A], A] 63 | 64 | def compare(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Int = c.toNumber(lhs).compare(rhs) 65 | def min(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Number = c.toNumber(lhs).min(rhs) 66 | def max(rhs: spire.math.Number)(implicit c: ConvertableFrom[A]): Number = c.toNumber(lhs).max(rhs) 67 | } 68 | 69 | final class SignedOps[A: Signed](lhs: A) { 70 | def abs: A = macro Ops.unop[A] 71 | def context_abs: A = macro Ops.unop[A] 72 | def sign: Sign = macro Ops.unop[Sign] 73 | def signum: Int = macro Ops.unop[Int] 74 | 75 | def isSignZero: Bool = macro Ops.unop[Bool] 76 | def isSignPositive: Bool = macro Ops.unop[Bool] 77 | def isSignNegative: Bool = macro Ops.unop[Bool] 78 | 79 | def isSignNonZero: Bool = macro Ops.unop[Bool] 80 | def isSignNonPositive: Bool = macro Ops.unop[Bool] 81 | def isSignNonNegative: Bool = macro Ops.unop[Bool] 82 | } 83 | 84 | final class IsRealOps[A <: Data](lhs: A)(implicit ev: IsReal[A]) { 85 | def isWhole: Bool = macro Ops.unop[Bool] 86 | def ceil: A = macro Ops.unop[A] 87 | def context_ceil: A = macro Ops.unop[A] 88 | def floor: A = macro Ops.unop[A] 89 | def round: A = macro Ops.unop[A] 90 | def truncate: A = ev.truncate(lhs) 91 | } 92 | 93 | class IsIntegerOps[A <: Data](lhs: A)(implicit ev: IsIntegral[A]) { 94 | def mod(rhs: A): A = ev.mod(lhs, rhs) 95 | def %(rhs: A): A = mod(rhs) 96 | def isOdd: Bool = ev.isOdd(lhs) 97 | def isEven: Bool = ev.isEven(lhs) 98 | } 99 | 100 | class ConvertableToOps[A <: Data](lhs: A)(implicit ev: ConvertableTo[A]) { 101 | def fromInt(i: Int): A = fromDouble(i.toDouble) 102 | def fromIntWithFixedWidth(i: Int): A = fromDoubleWithFixedWidth(i.toDouble) 103 | def fromDouble(d: Double): A = ev.fromDouble(d, lhs) 104 | def fromDoubleWithFixedWidth(d: Double): A = ev.fromDoubleWithFixedWidth(d, lhs) 105 | } 106 | 107 | class ChiselConvertableFromOps[A <: Data](lhs: A)(implicit ev: ChiselConvertableFrom[A]) { 108 | def intPart: SInt = ev.intPart(lhs) 109 | def asFixed: FixedPoint = ev.asFixed(lhs) 110 | def asFixed(proto: FixedPoint): FixedPoint = ev.asFixed(lhs, proto) 111 | def asReal: DspReal = ev.asReal(lhs) 112 | } 113 | 114 | class BinaryRepresentationOps[A <: Data](lhs: A)(implicit ev: BinaryRepresentation[A]) { 115 | def <<(n: Int): A = ev.shl(lhs, n) 116 | def <<(n: UInt): A = ev.shl(lhs, n) 117 | def >>(n: Int): A = ev.shr(lhs, n) 118 | def >>(n: UInt): A = ev.shr(lhs, n) 119 | def signBit: Bool = ev.signBit(lhs) 120 | def div2(n: Int): A = ev.div2(lhs, n) 121 | def mul2(n: Int): A = ev.mul2(lhs, n) 122 | def trimBinary(n: Int): A = ev.trimBinary(lhs, n) 123 | } 124 | 125 | class ContextualRingOps[A <: Data](lhs: A)(implicit ev: Ring[A]) { 126 | def context_+(rhs: A): A = ev.plusContext(lhs, rhs) 127 | def context_-(rhs: A): A = ev.minusContext(lhs, rhs) 128 | def context_*(rhs: A): A = ev.timesContext(lhs, rhs) 129 | def context_unary_- : A = ev.negateContext(lhs) 130 | } 131 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/implicits/ImplicitSyntax.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3.Data 6 | 7 | import scala.language.implicitConversions 8 | 9 | trait EqSyntax { 10 | implicit def eqOps[A <: Data: Eq](a: A): EqOps[A] = new EqOps(a) 11 | } 12 | 13 | trait PartialOrderSyntax extends EqSyntax { 14 | implicit def partialOrderOps[A <: Data: PartialOrder](a: A): PartialOrderOps[A] = new PartialOrderOps(a) 15 | } 16 | 17 | trait OrderSyntax extends PartialOrderSyntax { 18 | implicit def orderOps[A <: Data: Order](a: A): OrderOps[A] = new OrderOps(a) 19 | } 20 | 21 | trait SignedSyntax { 22 | implicit def signedOps[A <: Data: Signed](a: A): SignedOps[A] = new SignedOps(a) 23 | } 24 | 25 | trait IsRealSyntax extends OrderSyntax with SignedSyntax { 26 | implicit def isRealOps[A <: Data: IsReal](a: A): IsRealOps[A] = new IsRealOps(a) 27 | } 28 | 29 | trait IsIntegerSyntax extends IsRealSyntax { 30 | implicit def isIntegerOps[A <: Data: IsIntegral](a: A): IsIntegerOps[A] = new IsIntegerOps(a) 31 | } 32 | 33 | trait ConvertableToSyntax { 34 | implicit def convertableToOps[A <: Data: ConvertableTo](a: A): ConvertableToOps[A] = new ConvertableToOps(a) 35 | } 36 | 37 | trait ChiselConvertableFromSyntax { 38 | implicit def chiselConvertableFromOps[A <: Data: ChiselConvertableFrom](a: A): ChiselConvertableFromOps[A] = 39 | new ChiselConvertableFromOps(a) 40 | } 41 | 42 | trait BinaryRepresentationSyntax { 43 | implicit def binaryRepresentationOps[A <: Data: BinaryRepresentation](a: A): BinaryRepresentationOps[A] = 44 | new BinaryRepresentationOps(a) 45 | } 46 | 47 | trait ContextualRingSyntax { 48 | implicit def contextualRingOps[A <: Data: Ring](a: A): ContextualRingOps[A] = new ContextualRingOps(a) 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/implicits/ImplicitsTop.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | trait AllSyntax 6 | extends EqSyntax 7 | with PartialOrderSyntax 8 | with OrderSyntax 9 | with SignedSyntax 10 | with IsRealSyntax 11 | with IsIntegerSyntax 12 | with ConvertableToSyntax 13 | with ChiselConvertableFromSyntax 14 | with BinaryRepresentationSyntax 15 | with ContextualRingSyntax 16 | 17 | trait AllImpl extends UIntImpl with SIntImpl with FixedPointImpl with DspRealImpl with DspComplexImpl 18 | 19 | object implicits extends AllSyntax with AllImpl with spire.syntax.AllSyntax {} 20 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/implicits/package.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package object dsptools { 4 | import spire.implicits._ 5 | } 6 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/number_types/Numbers.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | 7 | /** 8 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 9 | * Java Bools. I suppose a more general solution would be generic in 10 | * return type, but the use cases there seem obscure. 11 | */ 12 | 13 | /** 14 | * A simple type class for numeric types that are a subset of the reals. 15 | */ 16 | trait IsReal[A <: Data] extends Any with Order[A] with Signed[A] { 17 | 18 | /** 19 | * Rounds `a` the nearest integer that is greater than or equal to `a`. 20 | */ 21 | def ceil(a: A): A 22 | 23 | /** 24 | * Rounds `a` the nearest integer that is less than or equal to `a`. 25 | */ 26 | def floor(a: A): A 27 | 28 | /** 29 | * Rounds `a` to the nearest integer 30 | * (When the fractional part is 0.5, tie breaking rounds to positive infinity i.e. round half up) 31 | */ 32 | def round(a: A): A 33 | 34 | /** 35 | * Returns `true` iff `a` is a an integer. 36 | */ 37 | def isWhole(a: A): Bool 38 | 39 | def truncate(a: A): A 40 | 41 | } 42 | 43 | object IsReal { 44 | def apply[A <: Data](implicit A: IsReal[A]): IsReal[A] = A 45 | } 46 | 47 | trait IsIntegral[A <: Data] extends Any with IsReal[A] { 48 | def ceil(a: A): A = a 49 | def floor(a: A): A = a 50 | def round(a: A): A = a 51 | def isWhole(a: A): Bool = true.B 52 | 53 | def mod(a: A, b: A): A 54 | 55 | def isOdd(a: A): Bool 56 | def isEven(a: A): Bool = !isOdd(a) 57 | def truncate(a: A): A = a 58 | } 59 | 60 | object IsIntegral { 61 | def apply[A <: Data](implicit A: IsIntegral[A]): IsIntegral[A] = A 62 | } 63 | 64 | ///////////////////////////////////////////////////////////////////////////////////// 65 | 66 | trait Real[A <: Data] extends Any with Ring[A] with ConvertableTo[A] with IsReal[A] { 67 | def fromRational(a: spire.math.Rational): A = fromDouble(a.toDouble) 68 | def fromAlgebraic(a: spire.math.Algebraic): A = fromDouble(a.toDouble) 69 | def fromReal(a: spire.math.Real): A = fromDouble(a.toDouble) 70 | } 71 | 72 | object Real { 73 | def apply[A <: Data](implicit A: Real[A]): Real[A] = A 74 | } 75 | 76 | /** 77 | * Much of this is drawn from non/spire, but using Chisel Bools instead of 78 | * Java Bools. I suppose a more general solution would be generic in 79 | * return type, but the use cases there seem obscure. 80 | */ 81 | 82 | trait Integer[A <: Data] extends Any with Real[A] with IsIntegral[A] 83 | 84 | object Integer { 85 | @inline final def apply[A <: Data](implicit ev: Integer[A]): Integer[A] = ev 86 | } 87 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/package.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | package object numbers 6 | extends AllSyntax 7 | with AllImpl 8 | with spire.syntax.RingSyntax 9 | /*with spire.syntax.AllSyntax*/ { 10 | type AdditiveGroup[T] = spire.algebra.AdditiveGroup[T] 11 | type CMonoid[T] = spire.algebra.CMonoid[T] 12 | type ConvertableFrom[T] = spire.math.ConvertableFrom[T] 13 | type Field[T] = spire.algebra.Field[T] 14 | type MultiplicativeAction[T, U] = spire.algebra.MultiplicativeAction[T, U] 15 | type MultiplicativeCMonoid[T] = spire.algebra.MultiplicativeCMonoid[T] 16 | 17 | val Multiplicative = spire.algebra.Multiplicative 18 | 19 | // rounding aliases 20 | val Floor = RoundDown 21 | val Ceiling = RoundUp 22 | val Convergent = RoundHalfToEven 23 | val Round = RoundHalfTowardsInfinity 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/dsptools/numbers/representations/BaseN.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers.representations 4 | 5 | object BaseN { 6 | 7 | /** Converts a decimal representation of the number n into a Seq of 8 | * Ints representing the base-r interpretation of n 9 | * NOTE: Least significant digit is highest indexed (right-most) due to recursion 10 | */ 11 | private def toDigitSeqInternal(n: Int, r: Int): Seq[Int] = { 12 | require(n >= 0, "n must be >= 0") 13 | require(r > 0, s"r $r must be > 0") 14 | // Least significant digit is right-most (resolved in this iteration) 15 | if (n == 0) Nil else toDigitSeqInternal(n / r, r) :+ (n % r) 16 | } 17 | 18 | /** Base-r representation of n, most significant digit 0-indexed */ 19 | def toDigitSeqMSDFirst(n: Int, r: Int): Seq[Int] = { 20 | // Highest digit first (left-most) 21 | val temp = toDigitSeqInternal(n, r) 22 | // Should return non-empty list 23 | if (temp.isEmpty) Seq(0) else temp 24 | } 25 | 26 | /** Zero pads Seq[Int] base-r representation */ 27 | def toDigitSeqMSDFirst(n: Int, r: Int, maxn: Int): Seq[Int] = { 28 | val digitSeq = toDigitSeqMSDFirst(n, r) 29 | val maxNumDigits = toDigitSeqMSDFirst(maxn, r).length 30 | val fillDigits = maxNumDigits - digitSeq.length 31 | val padding = List.fill(fillDigits)(0) 32 | padding ++ digitSeq 33 | } 34 | 35 | /** Returns # of Base r digits needed to represent the number n */ 36 | def numDigits(n: Int, r: Int): Int = toDigitSeqInternal(n, r).length 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/examples/StreamingAutocorrelator.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import dsptools.{hasContext, DspContext, Grow} 7 | import dsptools.examples.TransposedStreamingFIR 8 | import spire.algebra.Ring 9 | 10 | class StreamingAutocorrelator[T <: Data: Ring](inputGenerator: => T, outputGenerator: => T, delay: Int, windowSize: Int) 11 | extends Module 12 | with hasContext { 13 | // implicit val ev2 = ev(context) 14 | val io = IO(new Bundle { 15 | val input = Input(inputGenerator) 16 | val output = Output(outputGenerator) 17 | }) 18 | 19 | // create a sequence of registers (head is io.input) 20 | val delays = (0 until delay + windowSize).scanLeft(io.input) { 21 | case (left, _) => 22 | val nextReg = Reg(inputGenerator) 23 | nextReg := left 24 | nextReg 25 | } 26 | 27 | val window = delays.drop(delay + 1).reverse 28 | 29 | val firFilter = DspContext.withOverflowType(Grow) { 30 | Module(new TransposedStreamingFIR(inputGenerator, outputGenerator, inputGenerator, windowSize)) 31 | } 32 | 33 | firFilter.io.taps := window 34 | firFilter.io.input := io.input 35 | io.output := firFilter.io.output 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/examples/TransposedStreamingFIR.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.examples 4 | 5 | import chisel3._ 6 | import chisel3.util.Valid 7 | import spire.algebra.Ring 8 | import spire.implicits._ 9 | import spire.math.{ConvertableFrom, ConvertableTo} 10 | 11 | // CTTSF[FixedPoint, Double] 12 | // CTTSF[DspComplex[FixedPoint], scala.Complex[Double]] 13 | 14 | // This style preferred: 15 | // class CTTSF[T<:Data:Ring,V](i: T, o: T, val taps: Seq[V], conv: V=>T) 16 | 17 | class ConstantTapTransposedStreamingFIR[T <: Data: Ring: ConvertableTo, V: ConvertableFrom]( 18 | inputGenerator: T, 19 | outputGenerator: T, 20 | val taps: Seq[V]) 21 | extends Module { 22 | 23 | val io = IO(new Bundle { 24 | val input = Input(Valid(inputGenerator)) 25 | val output = Output(Valid(outputGenerator)) 26 | }) 27 | 28 | val products: Seq[T] = taps.reverse.map { tap => 29 | val t: T = implicitly[ConvertableTo[T]].fromType(tap) 30 | io.input.bits * t 31 | } 32 | 33 | val last = Reg[T](outputGenerator) 34 | val nextLast = products.reduceLeft { (left: T, right: T) => 35 | val reg = Reg(left.cloneType) 36 | when(io.input.valid) { 37 | reg := left 38 | } 39 | reg + right 40 | } 41 | when(io.input.valid) { 42 | last := nextLast 43 | } 44 | 45 | io.output.bits := last 46 | io.output.valid := RegNext(io.input.valid) 47 | } 48 | 49 | class TransposedStreamingFIR[T <: Data: Ring]( 50 | inputGenerator: => T, 51 | outputGenerator: => T, 52 | tapGenerator: => T, 53 | numberOfTaps: Int) 54 | extends Module { 55 | val io = IO(new Bundle { 56 | val input = Input(inputGenerator) // note, using as Input here, causes IntelliJ to not like '*' 57 | val output = Output(outputGenerator) 58 | val taps = Input(Vec(numberOfTaps, tapGenerator)) // note, using as Input here, causes IntelliJ to not like '*' 59 | }) 60 | 61 | val products: Seq[T] = io.taps.reverse.map { tap: T => 62 | io.input * tap 63 | } 64 | 65 | val last = Reg(products.head.cloneType) 66 | last := products.reduceLeft { (left: T, right: T) => 67 | val reg = Reg(left.cloneType) 68 | reg := left 69 | reg + right 70 | } 71 | 72 | io.output := last 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/examples/gainOffCorr.scala: -------------------------------------------------------------------------------- 1 | //// SPDX-License-Identifier: Apache-2.0 2 | // 3 | package dsptools.examples 4 | 5 | import chisel3._ 6 | 7 | import spire.algebra.Ring 8 | import spire.implicits._ 9 | 10 | //Simple implementation does the following 11 | // 1.Input value can be either real/imag 12 | // 2.Gain and offset either real/imag 13 | // 3.Assuming the number of input sources = number of lanes for now 14 | // 4.Assuming that the memory interface for gain and offset values will be done at a higher level 15 | 16 | class gainOffCorr[T <: Data: Ring](genIn: => T, genGain: => T, genOff: => T, genOut: => T, numLanes: Int) 17 | extends Module { 18 | val io = IO(new Bundle { 19 | val inputVal = Input(Vec(numLanes, genIn)) 20 | val gainCorr = Input(Vec(numLanes, genGain)) 21 | val offsetCorr = Input(Vec(numLanes, genOff)) 22 | val outputVal = Output(Vec(numLanes, genOut)) 23 | }) 24 | 25 | val inputGainCorr = io.inputVal.zip(io.gainCorr).map { case (in, gain) => in * gain } 26 | io.outputVal := inputGainCorr.zip(io.offsetCorr).map { case (inGainCorr, offset) => inGainCorr + offset } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/DspContextSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | import chisel3._ 6 | import dsptools.numbers._ 7 | import chiseltest._ 8 | import chiseltest.iotesters._ 9 | import dsptools.misc.PeekPokeDspExtensions 10 | import org.scalatest.freespec.AnyFreeSpec 11 | import org.scalatest.matchers.should.Matchers 12 | 13 | import scala.collection.parallel.CollectionConverters.RangeIsParallelizable 14 | 15 | class DspContextSpec extends AnyFreeSpec with ChiselScalatestTester with Matchers { 16 | "Context handling should be unobtrusive and convenient" - { 17 | "There should be a default available at all times" in { 18 | DspContext.current.binaryPoint should be(DspContext.defaultBinaryPoint) 19 | } 20 | 21 | "it can be very to override for simple alterations" in { 22 | DspContext.current.binaryPoint should be(DspContext.defaultBinaryPoint) 23 | 24 | DspContext.withBinaryPoint(-22) { 25 | DspContext.current.binaryPoint.get should be(-22) 26 | } 27 | 28 | DspContext.current.binaryPoint should be(DspContext.defaultBinaryPoint) 29 | } 30 | 31 | "it should be easy to override when using multiples" in { 32 | DspContext.current.binaryPoint should be(DspContext.defaultBinaryPoint) 33 | DspContext.current.overflowType should be(DspContext.defaultOverflowType) 34 | 35 | DspContext.alter(DspContext.current.copy(binaryPoint = Some(77), overflowType = Saturate)) { 36 | DspContext.current.binaryPoint.get should be(77) 37 | DspContext.current.overflowType should be(Saturate) 38 | } 39 | 40 | DspContext.current.binaryPoint should be(DspContext.defaultBinaryPoint) 41 | DspContext.current.overflowType should be(DspContext.defaultOverflowType) 42 | } 43 | 44 | "it should work multi-threaded and return values of block" ignore { 45 | DspContext.current.numBits should be(DspContext.defaultNumBits) 46 | 47 | val points = (1 to 100).par.map { n => 48 | DspContext.withNumBits(n) { 49 | DspContext.current.numBits.get should be(n) 50 | n * n 51 | } 52 | } 53 | 54 | val zipped = points.zipWithIndex 55 | zipped.foreach { 56 | case (p: Int, i: Int) => p should be(math.pow(i + 1, 2)) 57 | } 58 | 59 | DspContext.current.numBits should be(DspContext.defaultNumBits) 60 | } 61 | } 62 | 63 | "Test proper nesting of DspContext over module instantiation" in { 64 | test(new ContextNestingTop(UInt(4.W), UInt(5.W))) 65 | .runPeekPoke(new ContextNestingTester(_)) 66 | } 67 | } 68 | 69 | class ContextNestingTester(c: ContextNestingTop[UInt]) extends PeekPokeTester(c) with PeekPokeDspExtensions { 70 | poke(c.io.in1, 15.0) 71 | poke(c.io.in2, 2.0) 72 | 73 | expect(c.io.mod1Default, 1.0) 74 | expect(c.io.mod1Wrap, 1.0) 75 | expect(c.io.mod1Grow, 17.0) 76 | expect(c.io.mod2Default, 17.0) 77 | expect(c.io.mod2Wrap, 1.0) 78 | expect(c.io.mod2Grow, 17.0) 79 | } 80 | 81 | class ContextNestingBottom[T <: Data: Ring](gen1: T, gen2: T) extends Module { 82 | val io = IO(new Bundle { 83 | val in1 = Input(gen1) 84 | val in2 = Input(gen1) 85 | val outDefault = Output(gen2) 86 | val outWrap = Output(gen2) 87 | val outGrow = Output(gen2) 88 | }) 89 | 90 | DspContext.withOverflowType(Wrap) { 91 | io.outWrap := io.in1.context_+(io.in2) 92 | } 93 | DspContext.withOverflowType(Grow) { 94 | io.outGrow := io.in1.context_+(io.in2) 95 | } 96 | 97 | io.outDefault := io.in1.context_+(io.in2) 98 | } 99 | 100 | class ContextNestingTop[T <: Data: Ring](gen1: T, gen2: T) extends Module { 101 | val io = IO(new Bundle { 102 | val in1 = Input(gen1) 103 | val in2 = Input(gen1) 104 | val mod1Default = Output(gen2) 105 | val mod1Wrap = Output(gen2) 106 | val mod1Grow = Output(gen2) 107 | val mod2Default = Output(gen2) 108 | val mod2Wrap = Output(gen2) 109 | val mod2Grow = Output(gen2) 110 | }) 111 | 112 | private val mod1 = DspContext.withOverflowType(Wrap) { Module(new ContextNestingBottom(gen1, gen2)) } 113 | private val mod2 = DspContext.withOverflowType(Grow) { Module(new ContextNestingBottom(gen1, gen2)) } 114 | 115 | mod1.io.in1 := io.in1 116 | mod1.io.in2 := io.in2 117 | mod2.io.in1 := io.in1 118 | mod2.io.in2 := io.in2 119 | 120 | io.mod1Default := mod1.io.outDefault 121 | io.mod1Wrap := mod1.io.outWrap 122 | io.mod1Grow := mod1.io.outGrow 123 | io.mod2Default := mod2.io.outDefault 124 | io.mod2Wrap := mod2.io.outWrap 125 | io.mod2Grow := mod2.io.outGrow 126 | } 127 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/DspTesterUtilitiesSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | import dsptools.misc.DspTesterUtilities.signedToBigIntUnsigned 6 | import org.scalatest.flatspec.AnyFlatSpec 7 | import org.scalatest.matchers.should.Matchers 8 | 9 | import scala.math.{abs, pow} 10 | 11 | class DspTesterSpec {} 12 | 13 | class DspTesterUtilitiesSpec extends AnyFlatSpec with Matchers { 14 | 15 | behavior.of("Tester Converters") 16 | 17 | it should "convert positive and negative doubles to their BigInt, fixed point equivalents" in { 18 | 19 | def check_conversion(value: Double, totalWidth: Int, fractionalWidth: Int, verbose: Boolean = false): Unit = { 20 | if (verbose) { println(s"value = $value\ntotal width = $totalWidth\nfractional width = $fractionalWidth") } 21 | var bi = signedToBigIntUnsigned(value, totalWidth, fractionalWidth) 22 | if (verbose) { println(s"result = $bi") } 23 | // check sign, flip if necessary 24 | if (totalWidth > 0 && bi.testBit(totalWidth - 1)) { 25 | bi = -1 * ((bi ^ ((BigInt(1) << totalWidth) - 1)) + 1) 26 | } 27 | val bid = bi.toDouble / (BigInt(1) << fractionalWidth).toDouble 28 | if (verbose) { println(s"back to double = $bid") } 29 | val comp = scala.math.abs(bid - value) 30 | if (verbose) { println(s"comp = $comp") } 31 | val ref = scala.math.pow(2, -fractionalWidth) 32 | if (verbose) { println(s"ref = $ref") } 33 | require(abs(bid - value) < pow(2, -fractionalWidth)) 34 | } 35 | 36 | // integers 37 | var width = 14 38 | for (i <- -pow(2, width - 1).toInt until pow(2, width - 1).toInt) { 39 | check_conversion(i, width, 0) 40 | } 41 | 42 | // big integers 43 | width = 40 44 | for (i <- -pow(2, width - 1).toInt to pow(2, width - 1).toInt by pow(2, 20).toInt) { 45 | check_conversion(i, width, 0) 46 | } 47 | 48 | // total > fractional 49 | width = 19 50 | var fract = 8 51 | for (i <- BigDecimal(-pow(2, width - fract - 1)) to pow(2, width - fract - 1) - 1 by 1.0 / fract * 0.9) { 52 | check_conversion(i.toDouble, width, fract) 53 | } 54 | 55 | // total < fractional 56 | width = 11 57 | fract = 17 58 | for (i <- BigDecimal(-pow(2, width - fract - 1)) to pow(2, width - fract - 1) - 1 by 1.0 / fract * 0.9) { 59 | check_conversion(i.toDouble, width, fract) 60 | } 61 | 62 | } 63 | 64 | it should "fail to convert doubles to BigInts when not enough space is supplied" in { 65 | intercept[IllegalArgumentException] { signedToBigIntUnsigned(2.0, 4, 2) } 66 | intercept[IllegalArgumentException] { signedToBigIntUnsigned(-2.25, 4, 2) } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/ShiftRegisterDelaySpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools 4 | 5 | import chisel3._ 6 | import chiseltest._ 7 | import chiseltest.iotesters._ 8 | import dsptools.misc.PeekPokeDspExtensions 9 | import dsptools.numbers._ 10 | import fixedpoint._ 11 | import org.scalatest.freespec.AnyFreeSpec 12 | 13 | import scala.collection.mutable 14 | 15 | //TODO: DspReal truncate, ceil 16 | //TODO: FixedPoint ceil 17 | //TODO: For truncate and ceil, compare delay between Fixed and Real 18 | 19 | //scalastyle:off magic.number regex 20 | 21 | class AbsCircuitWithDelays[T <: Data: Signed](gen: T, val delays: Int) extends Module { 22 | val io = IO(new Bundle { 23 | val in = Input(gen) 24 | val outContextAbs = Output(gen) 25 | }) 26 | 27 | DspContext.withNumAddPipes(delays) { 28 | val con = io.in.context_abs 29 | printf("io.in %d con %d\n", io.in.asUInt, con.asUInt) 30 | io.outContextAbs := con 31 | } 32 | } 33 | 34 | class CeilTruncateCircuitWithDelays(val delays: Int) extends Module { 35 | val io = IO(new Bundle { 36 | val inFixed = Input(FixedPoint(12.W, 4.BP)) 37 | val inReal = Input(DspReal()) 38 | val outFixedCeil = Output(FixedPoint(12.W, 4.BP)) 39 | val outRealCeil = Output(DspReal()) 40 | val outFixedTruncate = Output(FixedPoint(12.W, 4.BP)) 41 | val outRealTruncate = Output(DspReal()) 42 | }) 43 | 44 | DspContext.withNumAddPipes(delays) { 45 | io.outFixedCeil := io.inFixed.ceil 46 | io.outRealCeil := io.inReal.context_ceil 47 | io.outFixedTruncate := io.inFixed.truncate 48 | io.outRealTruncate := io.inReal.truncate 49 | } 50 | } 51 | class CircuitWithDelaysTester[T <: Data: Signed](c: AbsCircuitWithDelays[T]) 52 | extends PeekPokeTester(c) 53 | with PeekPokeDspExtensions { 54 | private val delaySize = c.delays 55 | 56 | def oneTest(): Unit = { 57 | def values: Seq[Double] = (BigDecimal(-delaySize) to delaySize.toDouble by 1.0).map(_.toDouble) 58 | val inQueue = new mutable.Queue[Double] ++ values 59 | val outQueue = new mutable.Queue[Double] ++ Seq.fill(delaySize - 1)(0.0) ++ values.map(_.abs) 60 | 61 | while (inQueue.nonEmpty) { 62 | val inValue = inQueue.dequeue() 63 | poke(c.io.in, inValue) 64 | step(1) 65 | val expectedValue = outQueue.dequeue() 66 | expect(c.io.outContextAbs, expectedValue) 67 | } 68 | while (outQueue.nonEmpty) { 69 | val expectedValue = outQueue.dequeue() 70 | step(1) 71 | expect(c.io.outContextAbs, expectedValue) 72 | } 73 | } 74 | 75 | reset() 76 | poke(c.io.in, 0.0) 77 | step(10) 78 | oneTest() 79 | } 80 | 81 | class CeilTruncateTester(c: CeilTruncateCircuitWithDelays) extends PeekPokeTester(c) with PeekPokeDspExtensions { 82 | private val delaySize = c.delays 83 | 84 | def oneTest( 85 | inFixedIo: FixedPoint, 86 | outFixedIo: FixedPoint, 87 | inRealIo: DspReal, 88 | outRealIo: DspReal, 89 | delaySize: Int 90 | ): Unit = { 91 | def values: Seq[Double] = (BigDecimal(-delaySize) to delaySize.toDouble by 1.0).map(_.toDouble) 92 | val inQueue = new mutable.Queue[Double] ++ values 93 | val outQueue = new mutable.Queue[Double] ++ Seq.fill(delaySize)(0.0) ++ values.map(_.ceil) 94 | 95 | while (inQueue.nonEmpty) { 96 | val inValue = inQueue.dequeue() 97 | poke(inFixedIo, inValue) 98 | poke(inRealIo, inValue) 99 | val expectedValue = outQueue.dequeue() 100 | expect(outFixedIo, expectedValue) 101 | expect(outRealIo, expectedValue) 102 | step(1) 103 | } 104 | while (outQueue.nonEmpty) { 105 | val expectedValue = outQueue.dequeue() 106 | expect(outFixedIo, expectedValue) 107 | expect(outRealIo, expectedValue) 108 | step(1) 109 | } 110 | } 111 | 112 | poke(c.io.inFixed, 0.0) 113 | poke(c.io.inReal, 0.0) 114 | reset() 115 | step(10) 116 | oneTest(c.io.inFixed, c.io.outFixedCeil, c.io.inReal, c.io.outRealCeil, delaySize) 117 | } 118 | 119 | class ShiftRegisterDelaySpec extends AnyFreeSpec with ChiselScalatestTester { 120 | // Fails because FixedPoint's own ceil method is being used instead 121 | "ceil delay should be consistent between dsp real and fixed point" ignore { 122 | test(new CeilTruncateCircuitWithDelays(2)) 123 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 124 | .runPeekPoke(new CeilTruncateTester(_)) 125 | } 126 | 127 | "abs delays should be consistent across both sides of underlying mux" - { 128 | 129 | def sGen: SInt = SInt(16.W) 130 | def fGen: FixedPoint = FixedPoint(16.W, 8.BP) 131 | def rGen: DspReal = DspReal() 132 | 133 | "when used with SInt" in { 134 | test(new AbsCircuitWithDelays(sGen, 3)) 135 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 136 | .runPeekPoke(new CircuitWithDelaysTester(_)) 137 | } 138 | 139 | "when used with FixedPoint" in { 140 | test(new AbsCircuitWithDelays(fGen, 3)) 141 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 142 | .runPeekPoke(new CircuitWithDelaysTester(_)) 143 | } 144 | 145 | "when used with DspReal" in { 146 | test(new AbsCircuitWithDelays(rGen, 8)) 147 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 148 | .runPeekPoke(new CircuitWithDelaysTester(_)) 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/AbsSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import dsptools.{DspContext, Grow, Wrap} 8 | import org.scalatest.freespec.AnyFreeSpec 9 | import chiseltest._ 10 | import chiseltest.iotesters._ 11 | import dsptools.misc.PeekPokeDspExtensions 12 | 13 | class AbsSpec extends AnyFreeSpec with ChiselScalatestTester { 14 | "absolute value should work for all types" - { 15 | "abs should be obvious when not at extreme negative" - { 16 | "but returns a negative number for extreme value at max negative for SInt and FixedPoint" - { 17 | "with interpreter" in { 18 | test(new DoesAbs(UInt(4.W), SInt(4.W), FixedPoint(5.W, 2.BP))) 19 | .runPeekPoke(new DoesAbsTester(_)) 20 | } 21 | "and with verilator" in { 22 | test(new DoesAbs(UInt(4.W), SInt(4.W), FixedPoint(5.W, 2.BP))) 23 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 24 | .runPeekPoke(new DoesAbsTester(_)) 25 | } 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | class DoesAbsTester(c: DoesAbs[UInt, SInt, FixedPoint]) extends PeekPokeTester(c) with PeekPokeDspExtensions { 33 | for (i <- BigDecimal(0.0) to 15.0 by 1.0) { 34 | poke(c.io.uIn, i) 35 | expect(c.io.uAbsGrow, i) 36 | expect(c.io.uAbsWrap, i) 37 | step(1) 38 | } 39 | for (i <- BigDecimal(-7.0) to 7.0 by 1.0) { 40 | poke(c.io.sIn, i) 41 | expect(c.io.sAbsGrow, i.abs) 42 | expect(c.io.sAbsWrap, i.abs) 43 | step(1) 44 | } 45 | poke(c.io.sIn, -8.0) 46 | expect(c.io.sAbsGrow, 8.0) 47 | expect(c.io.sAbsWrap, -8.0) 48 | 49 | val increment = 0.25 50 | 51 | for (i <- BigDecimal(-3.75) to 3.75 by increment) { 52 | poke(c.io.fIn, i) 53 | expect(c.io.fAbsGrow, i.abs) 54 | expect(c.io.fAbsWrap, i.abs) 55 | step(1) 56 | } 57 | poke(c.io.fIn, -4.0) 58 | expect(c.io.fAbsGrow, 4.0) 59 | expect(c.io.fAbsWrap, -4.0) 60 | } 61 | 62 | class DoesAbs[TU <: Data: Signed: Ring, TS <: Data: Signed: Ring, TF <: Data: Signed: Ring]( 63 | uGen: TU, 64 | sGen: TS, 65 | fGen: TF) 66 | extends Module { 67 | val io = IO(new Bundle { 68 | val uIn = Input(uGen) 69 | val sIn = Input(sGen) 70 | val fIn = Input(fGen) 71 | 72 | val uAbsGrow = Output(uGen) 73 | val uAbsWrap = Output(uGen) 74 | 75 | val sAbsGrow = Output(SInt(5.W)) 76 | val sAbsWrap = Output(SInt(4.W)) 77 | 78 | val fAbsGrow = Output(FixedPoint(6.W, 2.BP)) 79 | val fAbsWrap = Output(FixedPoint(5.W, 2.BP)) 80 | }) 81 | 82 | io.uAbsGrow := DspContext.withOverflowType(Grow) { io.uIn.context_abs } 83 | io.uAbsWrap := DspContext.withOverflowType(Wrap) { io.uIn.context_abs } 84 | 85 | io.sAbsGrow := DspContext.withOverflowType(Grow) { io.sIn.context_abs } 86 | io.sAbsWrap := DspContext.withOverflowType(Wrap) { io.sIn.context_abs } 87 | 88 | io.fAbsGrow := DspContext.withOverflowType(Grow) { io.fIn.context_abs } 89 | io.fAbsWrap := DspContext.withOverflowType(Wrap) { io.fIn.context_abs } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/BaseNSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import dsptools.numbers.representations.BaseN 6 | import org.scalatest.flatspec.AnyFlatSpec 7 | import org.scalatest.matchers.should.Matchers 8 | 9 | class BaseNSpec extends AnyFlatSpec with Matchers { 10 | behavior.of("BaseN") 11 | it should "properly convert a decimal into BaseN" in { 12 | // n in decimal, rad = radix, res = expected representation in base rad 13 | case class BaseNTest(n: Int, rad: Int, res: Seq[Int]) 14 | 15 | // Most significant digit first (matched against WolframAlpha) 16 | val tests = Seq( 17 | BaseNTest(27, 4, Seq(1, 2, 3)), 18 | BaseNTest(17, 3, Seq(1, 2, 2)), 19 | BaseNTest(37, 5, Seq(1, 2, 2)) 20 | ) 21 | tests.foreach { 22 | case BaseNTest(n, rad, res) => 23 | require(BaseN.toDigitSeqMSDFirst(n, rad) == res, s"Base $rad conversion should work!") 24 | val paddedBaseN = BaseN.toDigitSeqMSDFirst(n, rad, 500) 25 | require( 26 | paddedBaseN == (Seq.fill(paddedBaseN.length - res.length)(0) ++ res), 27 | s"Padded base $rad conversion should work!" 28 | ) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/FixedPrecisionChangerSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import chiseltest._ 8 | import chiseltest.iotesters._ 9 | import dsptools.misc.PeekPokeDspExtensions 10 | import org.scalatest.freespec.AnyFreeSpec 11 | import org.scalatest.matchers.should.Matchers 12 | 13 | //scalastyle:off magic.number 14 | 15 | class FixedPrecisionChanger(inWidth: Int, inBinaryPoint: Int, outWidth: Int, outBinaryPoint: Int) extends Module { 16 | val io = IO(new Bundle { 17 | val in = Input(FixedPoint(inWidth.W, inBinaryPoint.BP)) 18 | val out = Output(FixedPoint(outWidth.W, outBinaryPoint.BP)) 19 | }) 20 | 21 | val reg = Reg(FixedPoint()) 22 | reg := io.in 23 | io.out := reg 24 | } 25 | 26 | class FixedPointTruncatorTester(c: FixedPrecisionChanger, inValue: Double, outValue: Double) 27 | extends PeekPokeTester(c) 28 | with PeekPokeDspExtensions { 29 | poke(c.io.in, inValue) 30 | step(1) 31 | expect(c.io.out, outValue, s"got ${peek(c.io.out)} should have $outValue") 32 | } 33 | 34 | class RemoveMantissa(inWidth: Int, inBinaryPoint: Int, outWidth: Int, outBinaryPoint: Int) extends Module { 35 | val io = IO(new Bundle { 36 | val in = Input(FixedPoint(inWidth.W, inBinaryPoint.BP)) 37 | val out = Output(FixedPoint(outWidth.W, 0.BP)) 38 | }) 39 | 40 | val reg = Reg(FixedPoint()) 41 | reg := io.in 42 | io.out := reg //.setBinaryPoint(0) 43 | } 44 | 45 | class RemoveMantissaTester(c: RemoveMantissa, inValue: Double, outValue: Double) 46 | extends PeekPokeTester(c) 47 | with PeekPokeDspExtensions { 48 | poke(c.io.in, inValue) 49 | step(1) 50 | expect(c.io.out, outValue, s"got ${peek(c.io.out)} should have $outValue") 51 | } 52 | 53 | class FixedPrecisionChangerSpec extends AnyFreeSpec with ChiselScalatestTester { 54 | "assignment of numbers with differing binary points seems to work as I would expect" - { 55 | "here we assign to a F8.1 from a F8.3" in { 56 | test(new FixedPrecisionChanger(8, 3, 8, 1)) 57 | .runPeekPoke(new FixedPointTruncatorTester(_, 6.875, 6.5)) 58 | } 59 | "here we assign to a F8.1 from a F8.1" - { 60 | "conversion to fixed point with less precision than poked value rounds up to 7, IS THIS RIGHT?" in { 61 | test(new FixedPrecisionChanger(8, 1, 8, 1)) 62 | .runPeekPoke(new FixedPointTruncatorTester(_, 6.875, 7.0)) 63 | } 64 | } 65 | "here we assign to a F10.6 from a F10.3" in { 66 | test(new FixedPrecisionChanger(10, 3, 10, 6)) 67 | .runPeekPoke(new FixedPointTruncatorTester(_, 6.875, 6.875)) 68 | } 69 | "let's try 1/3 just for fun with a big mantissa" - { 70 | "oops, this works because I built in a fudge factor for double comparison, how should this be done" in { 71 | test(new FixedPrecisionChanger(64, 58, 64, 16)) 72 | .runPeekPoke(new FixedPointTruncatorTester(_, 1.0 / 3.0, 0.3333282470703125)) 73 | } 74 | } 75 | } 76 | 77 | "removing mantissa can be done" - { 78 | "by using the setBinaryPoint Method" in { 79 | test(new RemoveMantissa(12, 4, 8, 0)) 80 | .runPeekPoke(new RemoveMantissaTester(_, 3.75, 3.0)) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/LnSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import chiseltest._ 7 | import chiseltest.iotesters._ 8 | import dsptools.misc.PeekPokeDspExtensions 9 | import org.scalatest.freespec.AnyFreeSpec 10 | 11 | class LnModule extends Module { 12 | val io = IO(new Bundle { 13 | val num = Input(DspReal()) 14 | val ln = Output(DspReal()) 15 | }) 16 | 17 | io.ln := io.num.ln 18 | } 19 | 20 | class LnTester(c: LnModule) extends PeekPokeTester(c) with PeekPokeDspExtensions { 21 | poke(c.io.num, 11.0) 22 | private val x = peek(c.io.ln) 23 | println(s"poked 1.0 got $x expected ${math.log(11.0)}") 24 | } 25 | 26 | class LnSpec extends AnyFreeSpec with ChiselScalatestTester { 27 | "ln should work" in { 28 | test(new LnModule) 29 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 30 | .runPeekPoke(new LnTester(_)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/ParameterizedOpSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import breeze.math.Complex 6 | import chisel3._ 7 | import fixedpoint._ 8 | import chiseltest._ 9 | import chiseltest.iotesters._ 10 | import org.scalatest.freespec.AnyFreeSpec 11 | import dsptools._ 12 | import dsptools.misc.PeekPokeDspExtensions 13 | 14 | //scalastyle:off magic.number 15 | 16 | object Operation extends Enumeration { 17 | type Operation = Value 18 | val Add, Sub, Mul = Value 19 | } 20 | import Operation.{Add, Mul, Sub} 21 | 22 | class ParameterizedNumberOperation[T <: Data: Ring]( 23 | inputGenerator: () => T, 24 | outputGenerator: () => T, 25 | val op: Operation.Value = Add) 26 | extends Module { 27 | val io = IO(new Bundle { 28 | val a1: T = Input(inputGenerator().cloneType) 29 | val a2: T = Input(inputGenerator().cloneType) 30 | val c: T = Output(outputGenerator().cloneType) 31 | }) 32 | 33 | val register1 = Reg(outputGenerator().cloneType) 34 | 35 | register1 := { 36 | op match { 37 | case Add => io.a1 + io.a2 38 | case Sub => io.a1 - io.a2 39 | case Mul => DspContext.withTrimType(NoTrim) { io.a1 * io.a2 } 40 | // case "/" => io.a1 / io.a2 41 | case _ => throw new Exception(s"Bad operator $op passed to ParameterizedNumberOperation") 42 | } 43 | } 44 | 45 | io.c := register1 46 | } 47 | 48 | class ParameterizedOpTester[T <: Data: Ring](c: ParameterizedNumberOperation[T]) 49 | extends PeekPokeTester(c) 50 | with PeekPokeDspExtensions { 51 | for { 52 | i <- BigDecimal(0.0) to 1.0 by 0.25 53 | j <- BigDecimal(0.0) to 4.0 by 0.5 54 | } { 55 | val expected = c.op match { 56 | case Add => i + j 57 | case Sub => i - j 58 | case Mul => i * j 59 | case _ => i + j 60 | } 61 | 62 | poke(c.io.a1, i) 63 | poke(c.io.a2, j) 64 | step(1) 65 | 66 | val result = peek(c.io.c) 67 | 68 | expect(c.io.c, expected, s"$i ${c.op} $j => $result, should have been $expected") 69 | } 70 | } 71 | 72 | class ParameterizedOpSpec extends AnyFreeSpec with ChiselScalatestTester { 73 | """ 74 | ParameterizedNumericOperation will 75 | """ - { 76 | def realGenerator(): DspReal = new DspReal 77 | def fixedInGenerator(): FixedPoint = FixedPoint(16.W, 8.BP) 78 | def fixedOutGenerator(): FixedPoint = FixedPoint(48.W, 8.BP) 79 | 80 | "process Real numbers with the basic mathematical operations" - { 81 | Seq(Add, Sub, Mul).foreach { operation => 82 | s"operation $operation should work for all inputs" in { 83 | test(new ParameterizedNumberOperation(realGenerator, realGenerator, operation)) 84 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 85 | .runPeekPoke(new ParameterizedOpTester(_)) 86 | } 87 | } 88 | } 89 | "process Fixed point numbers with the basic mathematical operations" - { 90 | Seq(Add, Sub, Mul).foreach { operation => 91 | s"operation $operation should work for all inputs" in { 92 | test(new ParameterizedNumberOperation(fixedInGenerator, fixedOutGenerator, operation)) 93 | .runPeekPoke(new ParameterizedOpTester(_)) 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | class ComplexOpTester[T <: DspComplex[_]](c: ParameterizedNumberOperation[T]) 101 | extends PeekPokeTester(c) 102 | with PeekPokeDspExtensions { 103 | for { 104 | i <- (BigDecimal(-1.0) to 1.0 by 0.25).map(_.toDouble) 105 | j <- (BigDecimal(-4.0) to 4.0 by 0.5).map(_.toDouble) 106 | } { 107 | val c1 = Complex(i, j) 108 | val c2 = Complex(j, i) 109 | 110 | val expected = c.op match { 111 | case Add => c1 + c2 112 | case Sub => c1 - c2 113 | case Mul => c1 * c2 114 | case _ => c1 + c2 115 | } 116 | 117 | poke(c.io.a1, c1) 118 | poke(c.io.a2, c2) 119 | step(1) 120 | 121 | val result = peek(c.io.c) 122 | 123 | expect(c.io.c, expected, s"$i ${c.op} $j => $result, should have been $expected") 124 | } 125 | } 126 | 127 | class ComplexOpSpec extends AnyFreeSpec with ChiselScalatestTester { 128 | """ 129 | ParameterizedNumericOperation will 130 | """ - { 131 | def complexFixedGenerator(): DspComplex[FixedPoint] = { 132 | DspComplex(FixedPoint(16.W, 2.BP), FixedPoint(16.W, 2.BP)) 133 | } 134 | 135 | def complexFixedOutputGenerator(): DspComplex[FixedPoint] = { 136 | DspComplex(FixedPoint(48.W, 4.BP), FixedPoint(48.W, 4.BP)) 137 | } 138 | 139 | def complexRealGenerator(): DspComplex[DspReal] = { 140 | DspComplex(DspReal(1.0), DspReal(1.0)) 141 | } 142 | 143 | "process DspComplex[Real] numbers with the basic mathematical operations" - { 144 | Seq(Add, Sub, Mul).foreach { operation => 145 | s"operation $operation should work for all inputs" in { 146 | test(new ParameterizedNumberOperation(complexRealGenerator, complexRealGenerator, operation)) 147 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 148 | .runPeekPoke(new ComplexOpTester(_)) 149 | } 150 | } 151 | } 152 | 153 | "process DspComplex[FixedPoint] numbers with the basic mathematical operations" - { 154 | Seq(Add, Sub, Mul).foreach { operation => 155 | s"operation $operation should work for all inputs" in { 156 | test(new ParameterizedNumberOperation(complexFixedGenerator, complexFixedOutputGenerator, operation)) 157 | .runPeekPoke(new ComplexOpTester(_)) 158 | } 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/test/scala/dsptools/numbers/TypeclassSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package dsptools.numbers 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import chiseltest._ 8 | import chiseltest.iotesters._ 9 | import dsptools.misc.PeekPokeDspExtensions 10 | import org.scalatest.freespec.AnyFreeSpec 11 | import org.scalatest.matchers.should.Matchers 12 | 13 | /* 14 | * These tests mostly exist to ensure that expressions of the form 15 | * Typeclass[T].function() 16 | * compile. Issue #17 talks about how this wasn't working for some 17 | * typeclasses 18 | */ 19 | 20 | class FuncModule[T <: Data](gen: T, func: T => T) extends Module { 21 | val io = IO(new Bundle { 22 | val in = Input(gen.cloneType) 23 | val out = Output(gen.cloneType) 24 | }) 25 | io.out := func(io.in) 26 | } 27 | 28 | object RingFunc { 29 | def apply[T <: Data: Ring](in: T): T = { 30 | val zero = Ring[T].zero 31 | val one = Ring[T].one 32 | Ring[T].times(one, Ring[T].plus(in, zero)) 33 | } 34 | } 35 | class RingModule[T <: Data: Ring](gen: T) extends FuncModule(gen, { in: T => RingFunc(in) }) 36 | 37 | object EqFunc { 38 | def apply[T <: Data: Eq: Ring](in: T): T = shadow.Mux(Eq[T].eqv(in, Ring[T].zero), Ring[T].one, in) 39 | } 40 | class EqModule[T <: Data: Eq: Ring](gen: T) extends FuncModule(gen, { in: T => EqFunc(in) }) 41 | 42 | object IntegerFunc { 43 | def apply[T <: Data: Integer](in: T): T = 44 | Integer[T].round(in) + IsIntegral[T].mod(in, in) 45 | } 46 | class IntegerModule[T <: Data: Integer](gen: T) extends FuncModule(gen, { in: T => IntegerFunc(in) }) 47 | 48 | object OrderFunc { 49 | def apply[T <: Data: Order](in: T): T = 50 | Order[T].min(in, in) 51 | } 52 | class OrderModule[T <: Data: Order](gen: T) extends FuncModule(gen, { in: T => OrderFunc(in) }) 53 | 54 | object PartialOrderFunc { 55 | def apply[T <: Data: PartialOrder](in: T): T = 56 | shadow.Mux(PartialOrder[T].partialCompare(in, in).bits.eq, in, in) 57 | } 58 | class PartialOrderModule[T <: Data: PartialOrder: Ring](gen: T) 59 | extends FuncModule(gen, { in: T => PartialOrderFunc(in) }) 60 | 61 | class SignedModule[T <: Data: Signed](gen: T) 62 | extends FuncModule( 63 | gen, 64 | { in: T => shadow.Mux(Signed[T].sign(in).neg, Signed[T].abs(in), shadow.Mux(Signed[T].sign(in).zero, in, in)) } 65 | ) 66 | 67 | class BinaryRepresentationModule[T <: Data: BinaryRepresentation](gen: T) 68 | extends FuncModule( 69 | gen, 70 | { in: T => (((in << 2) >> 1) << 3.U) >> 2.U } 71 | ) 72 | 73 | trait FuncTester[T <: Data, V] { 74 | def dut: FuncModule[T] 75 | def testInputs: Seq[V] 76 | def testOutputs: Seq[V] 77 | 78 | def myPoke(port: T, value: V): Unit 79 | def myExpect(port: T, value: V): Unit 80 | 81 | testInputs.zip(testOutputs).foreach { 82 | case (in, out) => 83 | myPoke(dut.io.in, in) 84 | myExpect(dut.io.out, out) 85 | } 86 | } 87 | 88 | class SIntFuncTester[T <: FuncModule[SInt]](dut: T, val testInputs: Seq[Int], val testOutputs: Seq[Int]) 89 | extends PeekPokeTester(dut) 90 | with PeekPokeDspExtensions 91 | with FuncTester[SInt, Int] { 92 | def myPoke(port: SInt, value: Int) = poke(port, value) 93 | def myExpect(port: SInt, value: Int) = expect(port, value) 94 | } 95 | 96 | class FixedPointFuncTester[T <: FuncModule[FixedPoint]]( 97 | dut: T, 98 | val testInputs: Seq[Double], 99 | val testOutputs: Seq[Double]) 100 | extends PeekPokeTester(dut) 101 | with PeekPokeDspExtensions 102 | with FuncTester[FixedPoint, Double] { 103 | def myPoke(port: FixedPoint, value: Double) = poke(port, value) 104 | def myExpect(port: FixedPoint, value: Double) = expect(port, value) 105 | } 106 | 107 | class DspRealFuncTester[T <: FuncModule[DspReal]](dut: T, val testInputs: Seq[Double], val testOutputs: Seq[Double]) 108 | extends PeekPokeTester(dut) 109 | with PeekPokeDspExtensions 110 | with FuncTester[DspReal, Double] { 111 | def myPoke(port: DspReal, value: Double) = poke(port, value) 112 | def myExpect(port: DspReal, value: Double) = expect(port, value) 113 | } 114 | 115 | class TypeclassSpec extends AnyFreeSpec with ChiselScalatestTester { 116 | "Ring[T].func() should work" in { 117 | test(new RingModule(SInt(10.W))) 118 | .runPeekPoke(new SIntFuncTester(_, Seq(2, -3), Seq(2, -3))) 119 | 120 | test(new RingModule(FixedPoint(10.W, 4.BP))) 121 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, -3), Seq(2, -3))) 122 | 123 | test(new RingModule(DspReal())) 124 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 125 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, -3), Seq(2, -3))) 126 | } 127 | "Eq[T].func() should work" in { 128 | test(new EqModule(SInt(10.W))) 129 | .runPeekPoke(new SIntFuncTester(_, Seq(2, 0), Seq(2, 1))) 130 | 131 | test(new EqModule(FixedPoint(10.W, 4.BP))) 132 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, 0), Seq(2, 1))) 133 | 134 | test(new EqModule(DspReal())) 135 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 136 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, 0), Seq(2, 1))) 137 | } 138 | "Integer[T].func() should work" in { 139 | test(new IntegerModule(SInt(10.W))) 140 | .runPeekPoke(new SIntFuncTester(_, Seq(2, -3), Seq(2, -3))) 141 | } 142 | "Order[T].func() should work" in { 143 | test(new OrderModule(SInt(10.W))) 144 | .runPeekPoke(new SIntFuncTester(_, Seq(2, -3), Seq(2, -3))) 145 | 146 | test(new OrderModule(FixedPoint(10.W, 4.BP))) 147 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, -3), Seq(2, -3))) 148 | 149 | test(new OrderModule(DspReal())) 150 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 151 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, -3), Seq(2, -3))) 152 | } 153 | "PartialOrder[T].func() should work" in { 154 | test(new PartialOrderModule(SInt(10.W))) 155 | .runPeekPoke(new SIntFuncTester(_, Seq(2, -3), Seq(2, -3))) 156 | 157 | test(new PartialOrderModule(FixedPoint(10.W, 4.BP))) 158 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, -3), Seq(2, -3))) 159 | 160 | test(new PartialOrderModule(DspReal())) 161 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 162 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, -3), Seq(2, -3))) 163 | } 164 | "Signed[T].func() should work" in { 165 | test(new SignedModule(SInt(10.W))) 166 | .runPeekPoke(new SIntFuncTester(_, Seq(2, -3), Seq(2, 3))) 167 | 168 | test(new SignedModule(FixedPoint(10.W, 4.BP))) 169 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, -3), Seq(2, 3))) 170 | 171 | test(new SignedModule(DspReal())) 172 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 173 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, -3), Seq(2, 3))) 174 | } 175 | "BinaryRepresentation[T].func() should work" in { 176 | test(new BinaryRepresentationModule(SInt(10.W))) 177 | .runPeekPoke(new SIntFuncTester(_, Seq(2, 3), Seq(8, 12))) 178 | 179 | test(new BinaryRepresentationModule(FixedPoint(10.W, 4.BP))) 180 | .runPeekPoke(new FixedPointFuncTester(_, Seq(2, 3), Seq(8, 12))) 181 | 182 | test(new BinaryRepresentationModule(DspReal())) 183 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 184 | .runPeekPoke(new DspRealFuncTester(_, Seq(2, 3), Seq(8, 12))) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/test/scala/examples/DspComplexSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import chisel3.testers.BasicTester 7 | import chiseltest._ 8 | import dsptools.numbers.DspComplex 9 | import org.scalatest.propspec.AnyPropSpec 10 | 11 | //scalastyle:off magic.number 12 | class DspComplexExamples extends Module { 13 | val io = IO(new Bundle { 14 | val in = Input(DspComplex(SInt(5.W), SInt(5.W))) 15 | val outJ = Output(DspComplex(SInt(5.W), SInt(5.W))) 16 | val inByJ = Output(DspComplex(SInt(5.W), SInt(5.W))) 17 | val inByJShortcut = Output(DspComplex(SInt(5.W), SInt(5.W))) 18 | }) 19 | 20 | io.outJ := DspComplex.j[SInt] 21 | io.inByJ := io.in * DspComplex.j[SInt] 22 | io.inByJShortcut := io.in.mulj() 23 | } 24 | 25 | class DspComplexExamplesTester extends BasicTester { 26 | val dut = Module(new DspComplexExamples) 27 | 28 | dut.io.in.real := 7.S 29 | dut.io.in.imag := (-4).S 30 | 31 | printf(s"inByJ.real: %d\n", dut.io.inByJ.real) 32 | printf(s"inByJ.imag: %d\n", dut.io.inByJ.imag) 33 | 34 | printf(s"inByJShortcut.real: %d\n", dut.io.inByJShortcut.real) 35 | printf(s"inByJShortcut.imag: %d\n", dut.io.inByJShortcut.imag) 36 | 37 | assert(dut.io.outJ.real === 0.S) 38 | assert(dut.io.outJ.imag === 1.S) 39 | 40 | assert(dut.io.inByJ.real === 4.S) 41 | assert(dut.io.inByJ.imag === 7.S) 42 | 43 | assert(dut.io.inByJShortcut.real === 4.S) 44 | assert(dut.io.inByJShortcut.imag === 7.S) 45 | 46 | stop() 47 | } 48 | 49 | class SIntTester extends BasicTester { 50 | val x = 10.S 51 | 52 | val xcopy = Wire(x.cloneType) 53 | xcopy := x 54 | 55 | assert(x === xcopy) 56 | 57 | val y = DspComplex((-4).S, (-1).S) 58 | 59 | assert(y.real === (-4).S) 60 | assert(y.imag === (-1).S) 61 | 62 | stop() 63 | } 64 | 65 | class DspComplexSpec extends AnyPropSpec with ChiselScalatestTester { 66 | property("using j with complex numbers should work") { 67 | test(new DspComplexExamplesTester) 68 | .runUntilStop() 69 | } 70 | 71 | property("assigning Wire(SInt) should work") { 72 | test(new SIntTester) 73 | .runUntilStop() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/scala/examples/ParameterizedAdderSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import chiseltest._ 8 | import chiseltest.iotesters.PeekPokeTester 9 | import dsptools.misc.PeekPokeDspExtensions 10 | import dsptools.numbers._ 11 | import org.scalatest.flatspec.AnyFlatSpec 12 | 13 | //noinspection TypeAnnotation 14 | class ParameterizedAdder[T <: Data: Ring](gen: () => T) extends Module { 15 | val a1: T = IO(Input(gen().cloneType)) 16 | val a2: T = IO(Input(gen().cloneType)) 17 | val c = IO(Output(gen().cloneType)) 18 | 19 | val register1 = Reg(gen().cloneType) 20 | 21 | register1 := a1 + a2 22 | 23 | c := register1 24 | } 25 | 26 | class ParameterizedAdderTester[T <: Data: Ring](c: ParameterizedAdder[T]) 27 | extends PeekPokeTester(c) 28 | with PeekPokeDspExtensions { 29 | for { 30 | i <- (BigDecimal(-2.0) to 1.0 by 0.25).map(_.toDouble) 31 | j <- (BigDecimal(-2.0) to 4.0 by 0.5).map(_.toDouble) 32 | } { 33 | poke(c.a1, i) 34 | poke(c.a2, j) 35 | step(1) 36 | 37 | val result = peek(c.c) 38 | 39 | expect(c.c, i + j, s"parameterize adder tester $i + $j => $result should have been ${i + j}") 40 | } 41 | } 42 | 43 | class ParameterizedAdderSpec extends AnyFlatSpec with ChiselScalatestTester { 44 | 45 | behavior.of("parameterized adder circuit on blackbox real") 46 | 47 | it should "allow registers to be declared that infer widths" in { 48 | def getReal: DspReal = new DspReal 49 | 50 | test(new ParameterizedAdder(() => getReal)) 51 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 52 | .runPeekPoke(new ParameterizedAdderTester(_)) 53 | } 54 | 55 | behavior.of("parameterized adder circuit on fixed point") 56 | 57 | it should "allow registers to be declared that infer widths" in { 58 | def getFixed: FixedPoint = FixedPoint(32.W, 16.BP) 59 | 60 | test(new ParameterizedAdder(() => getFixed)).runPeekPoke(new ParameterizedAdderTester(_)) 61 | 62 | test(new ParameterizedAdder(() => getFixed)) 63 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 64 | .runPeekPoke(new ParameterizedAdderTester(_)) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/scala/examples/ParameterizedSaturatingAdderSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import chiseltest._ 7 | import chiseltest.iotesters._ 8 | import dsptools.misc.PeekPokeDspExtensions 9 | import dsptools.{DspContext, Saturate} 10 | import dsptools.numbers._ 11 | import org.scalatest.flatspec.AnyFlatSpec 12 | 13 | class ParameterizedSaturatingAdder[T <: Data: Integer](gen: => T) extends Module { 14 | val io = IO(new Bundle { 15 | val a1: T = Input(gen.cloneType) 16 | val a2: T = Input(gen.cloneType) 17 | val normalSum = Output(gen.cloneType) 18 | val saturatedSum = Output(gen.cloneType) 19 | }) 20 | 21 | val register1 = Reg(gen.cloneType) 22 | val register2 = Reg(gen.cloneType) 23 | 24 | println(s"ParameterizedSaturatingAdder ${DspContext.current}") 25 | register1 := io.a1 + io.a2 26 | 27 | DspContext.withOverflowType(Saturate) { 28 | register2 := io.a1 + io.a2 29 | } 30 | io.normalSum := register1 31 | io.saturatedSum := register2 32 | } 33 | 34 | class ParameterizedSaturatingAdderTester[T <: Data: Integer](c: ParameterizedSaturatingAdder[T], width: Int) 35 | extends PeekPokeTester(c) 36 | with PeekPokeDspExtensions { 37 | 38 | val min = -(1 << (width - 1)) 39 | val max = (1 << (width - 1)) - 1 40 | println("Min = " + min.toString) 41 | println("Max = " + max.toString) 42 | def overflowint(x: Int): Int = { 43 | if (x > max) overflowint(min + x - max - 1) 44 | else if (x < min) overflowint(max + x - min + 1) 45 | else x 46 | } 47 | def saturateint(x: Int): Int = { 48 | if (x > max) max 49 | else if (x < min) min 50 | else x 51 | } 52 | for { 53 | i <- min to max 54 | j <- min to max 55 | } { 56 | println(s"I=$i and J=$j") 57 | poke(c.io.a1, i) 58 | poke(c.io.a2, j) 59 | step(1) 60 | 61 | val resultNormal = peek(c.io.normalSum) 62 | val resultSaturated = peek(c.io.saturatedSum) 63 | 64 | expect( 65 | c.io.normalSum, 66 | overflowint(i + j), 67 | s"parameterized normal adder $i + $j => $resultNormal should have been ${overflowint(i + j)}" 68 | ) 69 | expect( 70 | c.io.saturatedSum, 71 | saturateint(i + j), 72 | s"parameterized saturating adder $i + $j => $resultSaturated should have been ${saturateint(i + j)}" 73 | ) 74 | } 75 | } 76 | 77 | class ParameterizedSaturatingAdderSpec extends AnyFlatSpec with ChiselScalatestTester { 78 | behavior.of("parameterized saturating adder circuit on SInt") 79 | 80 | it should "allow registers to be declared that infer widths" ignore { 81 | 82 | val width = 3 83 | def getSInt: SInt = SInt(width.W) 84 | 85 | test(new ParameterizedSaturatingAdder(getSInt)) 86 | .runPeekPoke(new ParameterizedSaturatingAdderTester(_, width)) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/test/scala/examples/RealAdderSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import chiseltest._ 7 | import chiseltest.iotesters._ 8 | import dsptools.misc.PeekPokeDspExtensions 9 | import dsptools.numbers.DspReal 10 | import org.scalatest.flatspec.AnyFlatSpec 11 | 12 | class RealAdder extends Module { 13 | val io = IO(new Bundle { 14 | val a1 = Input(new DspReal) 15 | val a2 = Input(new DspReal) 16 | val c = Output(new DspReal) 17 | }) 18 | 19 | val register1 = Reg(new DspReal) 20 | 21 | register1 := io.a1 + io.a2 22 | 23 | io.c := register1 24 | } 25 | 26 | class RealAdderTester(c: RealAdder) extends PeekPokeTester(c) with PeekPokeDspExtensions { 27 | for { 28 | i <- (BigDecimal(0.0) to 1.0 by 0.25).map(_.toDouble) 29 | j <- (BigDecimal(0.0) to 4.0 by 0.5).map(_.toDouble) 30 | } { 31 | poke(c.io.a1, i) 32 | poke(c.io.a2, j) 33 | step(1) 34 | 35 | expect(c.io.c, i + j) 36 | } 37 | } 38 | 39 | class RealAdderSpec extends AnyFlatSpec with ChiselScalatestTester { 40 | behavior.of("adder circuit on blackbox real") 41 | 42 | it should "allow registers to be declared that infer widths" in { 43 | test(new RealAdder) 44 | .withAnnotations(Seq(VerilatorBackendAnnotation)) 45 | .runPeekPoke(new RealAdderTester(_)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/scala/examples/SimpleAdderSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import chiseltest._ 8 | import chiseltest.iotesters.PeekPokeTester 9 | import org.scalatest.flatspec.AnyFlatSpec 10 | import dsptools.misc.PeekPokeDspExtensions 11 | 12 | //noinspection TypeAnnotation 13 | class SimpleAdder extends Module { 14 | val a1 = IO(Input(FixedPoint(6.W, 4.BP))) 15 | val a2 = IO(Input(FixedPoint(8.W, 1.BP))) 16 | val c = IO(Output(FixedPoint(12.W, 5.BP))) 17 | 18 | val register1 = Reg(FixedPoint()) 19 | 20 | register1 := a1 + a2 21 | 22 | c := register1 23 | } 24 | 25 | class SimpleAdderTester(c: SimpleAdder) extends PeekPokeTester(c) with PeekPokeDspExtensions { 26 | for { 27 | i <- BigDecimal(0.0) to 1.0 by 0.25 28 | j <- BigDecimal(0.0) to 4.0 by 0.5 29 | } { 30 | val expected = i + j 31 | 32 | poke(c.a1, i) 33 | poke(c.a2, j) 34 | step(1) 35 | expect(c.c, expected, s"SimpleAdder: $i + $j should make $expected got ${peek(c.c)}") 36 | } 37 | } 38 | class SimpleAdderSpec extends AnyFlatSpec with ChiselScalatestTester { 39 | behavior.of("SimpleAdder") 40 | 41 | it should "add to numbers excellently" in { 42 | test(new SimpleAdder) //(new SimpleAdderTester(_)) 43 | .runPeekPoke(new SimpleAdderTester(_)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/scala/examples/SimpleCaseClassBundleSpec.scala: -------------------------------------------------------------------------------- 1 | //// SPDX-License-Identifier: Apache-2.0 2 | // 3 | package examples 4 | 5 | import chisel3._ 6 | import chiseltest._ 7 | import chiseltest.iotesters._ 8 | import org.scalatest.flatspec.AnyFlatSpec 9 | 10 | //scalastyle:off magic.number 11 | 12 | //case class CaseClassBundle(a: SInt) extends Bundle 13 | //case class CaseClassBundle(underlying: SInt) extends Bundle { 14 | // val underlying = gen.cloneType 15 | // override def cloneType: this.type = new CaseClassBundle(underlying.cloneType).asInstanceOf[this.type] 16 | //} 17 | class CaseClassBundle(gen: SInt) extends Bundle { 18 | val underlying = gen 19 | } 20 | 21 | class SimpleCaseClassModule(gen: SInt) extends Module { 22 | val io = IO(new Bundle { 23 | val in = Input(new CaseClassBundle(gen)) 24 | val out = Output(new CaseClassBundle(gen)) 25 | }) 26 | 27 | val register1 = Reg(io.out.cloneType) 28 | 29 | register1 := io.in 30 | 31 | io.out := register1 32 | } 33 | 34 | class SimpleCaseClassBundleTester(c: SimpleCaseClassModule) extends PeekPokeTester(c) { 35 | 36 | poke(c.io.in.underlying, 7) 37 | step(1) 38 | expect(c.io.out.underlying, 7) 39 | } 40 | 41 | class SimpleCaseClassBundleSpec extends AnyFlatSpec with ChiselScalatestTester { 42 | behavior.of("SimpleCaseClassBundle") 43 | 44 | it should "push number through with one step delay" in { 45 | test(new SimpleCaseClassModule(SInt(5.W))) 46 | .runPeekPoke(new SimpleCaseClassBundleTester(_)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/scala/examples/SimpleComplexMultiplierSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | import chisel3._ 6 | import fixedpoint._ 7 | import chiseltest._ 8 | import chiseltest.iotesters._ 9 | import chisel3.{Bundle, Module} 10 | import dsptools.misc.PeekPokeDspExtensions 11 | import dsptools.numbers._ 12 | import org.scalatest.flatspec.AnyFlatSpec 13 | 14 | //scalastyle:off magic.number 15 | class SimpleComplexMultiplier extends Module { 16 | val io = IO(new Bundle { 17 | val a1 = Input(DspComplex(FixedPoint(6.W, 4.BP), FixedPoint(6.W, 4.BP))) 18 | val a2 = Input(DspComplex(FixedPoint(8.W, 1.BP), FixedPoint(8.W, 1.BP))) 19 | val c = Output(DspComplex(FixedPoint(14.W, 5.BP), FixedPoint(14.W, 5.BP))) 20 | }) 21 | // spatialAssert(Seq(io.a1), Seq(io.c), 5) 22 | // spatialAssert(Seq(io.a2), Seq(io.c), "group1") 23 | 24 | val register1 = Reg(io.c.cloneType) 25 | 26 | // val registerReal = Reg(io.a1.real) 27 | // val registerimag = Reg(io.a1.imag) 28 | 29 | register1 := io.a1 * io.a2 30 | 31 | io.c := register1 32 | } 33 | 34 | class SimpleComplexMultiplierTester(c: SimpleComplexMultiplier) extends PeekPokeTester(c) with PeekPokeDspExtensions { 35 | for { 36 | i <- (BigDecimal(0.0) to 1.0 by 0.25).map(_.toDouble) 37 | j <- (BigDecimal(0.0) to 4.0 by 0.5).map(_.toDouble) 38 | } { 39 | val expected = i * j 40 | 41 | poke(c.io.a1.real, i) 42 | poke(c.io.a1.imag, 0.0) 43 | poke(c.io.a2.real, j) 44 | poke(c.io.a2.imag, 0.0) 45 | step(1) 46 | 47 | expect(c.io.c.real, i * j) 48 | 49 | println(s"SimpleComplexMultiplier: $i * $j should make $expected got ${peek(c.io.c.real)}") 50 | } 51 | } 52 | 53 | class SimpleComplexMultiplierSpec extends AnyFlatSpec with ChiselScalatestTester { 54 | behavior.of("SimpleComplexMultiplier") 55 | 56 | it should "multiply complex numbers excellently" in { 57 | test(new SimpleComplexMultiplier) 58 | .runPeekPoke(new SimpleComplexMultiplierTester(_)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/scala/examples/SimpleDspModuleSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | // Allows you to use Chisel Module, Bundle, etc. 6 | import chisel3._ 7 | import dsptools.misc.PeekPokeDspExtensions 8 | // Allows you to use FixedPoint 9 | import fixedpoint._ 10 | // If you want to take advantage of type classes >> Data:RealBits (i.e. pass in FixedPoint or DspReal) 11 | import dsptools.numbers._ 12 | // Enables you to set DspContext's for things like overflow behavior, rounding modes, etc. 13 | import dsptools.DspContext 14 | // Use chiseltest 15 | import chiseltest._ 16 | // Use chiseltest's iotesters inferface 17 | import chiseltest.iotesters._ 18 | // Scala unit testing style 19 | import org.scalatest.flatspec.AnyFlatSpec 20 | 21 | // IO Bundle. This also creates x, y, z inputs/outputs (direction must be specified at some IO hierarchy level) 22 | // of the type you specify via gen (must be Data:RealBits = UInt, SInt, FixedPoint, DspReal) 23 | class SimpleDspIo[T <: Data: RealBits](gen: T) extends Bundle { 24 | val x = Input(gen.cloneType) 25 | val y = Input(gen.cloneType) 26 | val z = Output(gen.cloneType) 27 | } 28 | 29 | // Parameterized Chisel Module; takes in type parameters as explained above 30 | class SimpleDspModule[T <: Data: RealBits](gen: T, val addPipes: Int) extends Module { 31 | // This is how you declare an IO with parameters 32 | val io = IO(new SimpleDspIo(gen)) 33 | // Output will be current x + y addPipes clk cycles later 34 | // Note that this relies on the fact that type classes have a special + that 35 | // add addPipes # of ShiftRegister after the sum. If you don't wrap the sum in 36 | // DspContext.withNumAddPipes(addPipes), the default # of addPipes is used. 37 | DspContext.withNumAddPipes(addPipes) { 38 | io.z := io.x.context_+(io.y) 39 | } 40 | } 41 | 42 | // You create a tester that must extend PeekPokeDspExtensions to support Dsp type peeks/pokes (with doubles, complex, etc.) 43 | class SimpleDspModuleTester[T <: Data: RealBits](c: SimpleDspModule[T]) 44 | extends PeekPokeTester(c) 45 | with PeekPokeDspExtensions { 46 | val x = Seq(-1.1, -0.4, 0.4, 1.1) 47 | val z = x.map(2 * _) 48 | for (i <- 0 until (x.length + c.addPipes)) { 49 | val in = x(i % x.length) 50 | // Feed in to the x, y inputs 51 | poke(c.io.x, in) 52 | poke(c.io.y, in) 53 | 54 | if (i >= c.addPipes) { 55 | // Expect that the z output matches the expected value @ z(i - c.addPipes) to some tolerance 56 | // as described below 57 | expect(c.io.z, z(i - c.addPipes)) 58 | } 59 | // Step the clock by 1 period 60 | step(1) 61 | } 62 | } 63 | 64 | // Scala style testing 65 | class SimpleDspModuleSpec extends AnyFlatSpec with ChiselScalatestTester { 66 | 67 | behavior.of("simple dsp module") 68 | 69 | it should "properly add fixed point types" ignore { 70 | // Run the dsp tester by following this style: You need to pass in the module to test. Note that here, you're 71 | // testing the module with inputs/outputs of FixedPoint type (Q15.12) and 3 registers (for retiming) at the output. 72 | // You could alternatively use DspReal() 73 | // Scala keeps track of which tests pass/fail. 74 | // Supposedly, Chisel3 testing infrastructure might be overhauled to reduce the amount of boilerplate, 75 | // but this is currently the endorsed way to do things. 76 | test(new SimpleDspModule(FixedPoint(16.W, 12.BP), addPipes = 3)) 77 | .runPeekPoke(new SimpleDspModuleTester(_)) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/scala/examples/StreamingAutocorrelatorSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | //scalastyle:off magic.number 6 | 7 | import chisel3._ 8 | import chiseltest._ 9 | import chiseltest.iotesters._ 10 | import dsptools.misc.PeekPokeDspExtensions 11 | import dsptools.numbers.implicits._ 12 | import org.scalatest.flatspec.AnyFlatSpec 13 | 14 | class StreamingAutocorrelatorTester(c: StreamingAutocorrelator[SInt]) 15 | extends PeekPokeTester(c) 16 | with PeekPokeDspExtensions { 17 | 18 | for (num <- -5 to 5) { 19 | poke(c.io.input, BigInt(num)) 20 | step(1) 21 | println(peek(c.io.output).toString()) 22 | } 23 | } 24 | 25 | class StreamingAutocorrelatorSpec extends AnyFlatSpec with ChiselScalatestTester { 26 | "StreamingAutocorrelatorFIR" should "compute a running average like thing" in { 27 | val taps = Seq.tabulate(3) { x => x.S } 28 | //implicit val DefaultDspContext = DspContext() 29 | //implicit val evidence = (context :DspContext) => new SIntRing()(context) 30 | 31 | test(new StreamingAutocorrelator(SInt(10.W), SInt(20.W), 2, 3)) 32 | .runPeekPoke(new StreamingAutocorrelatorTester(_)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/scala/examples/TransposedStreamFIRSpec.scala: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | package examples 4 | 5 | //scalastyle:off magic.number 6 | 7 | import chisel3._ 8 | import chiseltest._ 9 | import chiseltest.iotesters._ 10 | import dsptools.numbers.implicits._ 11 | import org.scalatest.flatspec.AnyFlatSpec 12 | import dsptools.examples.{ConstantTapTransposedStreamingFIR, TransposedStreamingFIR} 13 | 14 | class ConstantTapTransposedStreamingTester(c: ConstantTapTransposedStreamingFIR[SInt, Int]) extends PeekPokeTester(c) { 15 | val smallest = -5 16 | val biggest = 5 17 | println(s"Taps are ${c.taps.toString}") 18 | 19 | def checkAnswer(n: Int): Int = { 20 | // assumes inputs increase by 1 each time 21 | c.taps.zipWithIndex.foldLeft(0) { 22 | case (s, (tap, idx)) => 23 | s + tap * (if (n - idx >= smallest) n - idx else 0) 24 | } 25 | } 26 | // initialize old state to 0 27 | poke(c.io.input.valid, 1) 28 | poke(c.io.input.bits, BigInt(0)) 29 | step(c.taps.length) 30 | 31 | for (num <- smallest to biggest) { 32 | poke(c.io.input.bits, BigInt(-7)) 33 | poke(c.io.input.valid, 0) 34 | for (i <- 0 until 10) { 35 | step(1) 36 | expect(c.io.output.valid, 0, "Output should not be valid if input is invalid") 37 | } 38 | poke(c.io.input.valid, 1) 39 | poke(c.io.input.bits, BigInt(num)) 40 | step(1) 41 | println(peek(c.io.output.bits).toString()) 42 | println(s"Answer should be ${checkAnswer(num)}") 43 | expect(c.io.output.bits, checkAnswer(num), "Output did should match expected data") 44 | expect(c.io.output.valid, 1, "Output should be valid if input is valid") 45 | } 46 | 47 | } 48 | 49 | class TransposedStreamingTester(c: TransposedStreamingFIR[SInt]) extends PeekPokeTester(c) { 50 | 51 | for (num <- -5 to 5) { 52 | poke(c.io.input, BigInt(num)) 53 | step(1) 54 | println(peek(c.io.output).toString()) 55 | } 56 | } 57 | 58 | class TransposedStreamFIRSpec extends AnyFlatSpec with ChiselScalatestTester { 59 | "ConstantTapTransposedStreamingFIR" should "compute a running average like thing" in { 60 | val taps = 0 until 3 61 | 62 | test(new ConstantTapTransposedStreamingFIR(SInt(10.W), SInt(16.W), taps)) 63 | .runPeekPoke(new ConstantTapTransposedStreamingTester(_)) 64 | } 65 | } 66 | --------------------------------------------------------------------------------