├── .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 |
--------------------------------------------------------------------------------