├── .devcontainer.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── 1.1-Spinal-quick-start.ipynb ├── 1.2-Spinal-design-Rules-error-Check.ipynb ├── 2.1-Spinal-core-Data-type.ipynb ├── 2.2-Spinal-core-Combinational.ipynb ├── 2.3-Spinal-core-Sequential.ipynb ├── 2.4-Spinal-core-FixPoint.ipynb ├── 2.5-Spinal-core-Width-propogate.ipynb ├── 2.6-Spinal-core-Component-Function-Area.ipynb ├── 2.7-Spinal-core-Clock-domain.ipynb ├── 3.1-Spinal-lib-flow-stream.ipynb ├── 3.2-Spinal-lib-AMBA-bus.ipynb ├── 3.3-Spinal-lib-Regif.ipynb ├── 3.4-Spinal-lib-Soc.ipynb ├── 3.5-Spinal-lib-VexRiscv.ipynb ├── 3.6-Spinal-memWrapper.ipynb ├── 4.1-Spinal-Example-Simple-ones.ipynb ├── 4.2-Spinal-Example-Intermediate-ones.ipynb ├── 4.3-Spinal-Example-Advanced-ones.ipynb ├── 5.2-Spinal-FAQ.ipynb ├── 5.3-Spinal-Utils.ipynb ├── 6.1-env-chisel.ipynb ├── 6.2-env-breeze.ipynb ├── 7.1-Scala-case-pattern.ipynb ├── 7.2-Scala-implicit.ipynb ├── 7.3-Scala-reflect.ipynb ├── 7.4-Scala-Macro.ipynb ├── 7.5-Scala-DataType.ipynb ├── 7.6-Scala-FAQ.ipynb ├── README.md ├── Toc.md ├── binder ├── apt.txt └── postBuild ├── exercises ├── .scalafix.conf ├── Dockerfile ├── Examples │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── exercises │ │ │ ├── ApbArbiter.scala │ │ │ ├── ApbBridge.scala │ │ │ ├── ApbBridgeSim.scala │ │ │ ├── Booth.scala │ │ │ ├── FifoCC.scala │ │ │ ├── FifoCCSim.scala │ │ │ ├── HandShakePipe.scala │ │ │ └── WeightedArbiter.scala │ └── test │ │ └── src │ │ └── scala │ │ └── exercises │ │ ├── ApbArbiterTest.scala │ │ ├── ApbBridgeTest.scala │ │ ├── FifoCCTest.scala │ │ ├── HandShakePipeTest.scala │ │ └── WeightedArbiterTest.scala ├── build.sc └── run.sh ├── rtl └── .keep └── source ├── Index.org ├── InterruptRegIf2.html ├── SpinalHDL.png ├── a.gif ├── addertree1.png ├── addertree2.png ├── b.gif ├── coursier ├── custom.js ├── jupyter.png ├── load-spinal.sc ├── regif-gen-doc.png ├── regif-gen-int.png ├── sin.svg └── sine.png /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "datenlord/spinal-cocotb:1.6.0", 3 | "extensions": ["scalameta.metals"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: [dev] 5 | schedule: [cron: "3 */24 * * *"] 6 | 7 | jobs: 8 | build-and-simulate: 9 | name: Build and Simulate 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: olafurpg/setup-scala@v10 14 | with: 15 | java-version: adopt@1.11 16 | - name: Run 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install -y verilator 20 | ./exercises/run.sh 21 | - name: Setup tmate session 22 | if: ${{ failure() }} 23 | uses: mxschmitt/action-tmate@v3 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.v 2 | *.vhdl 3 | *.log 4 | .ipynb_checkpoints/ 5 | rtl/*.* 6 | bak/ 7 | zh2/ 8 | zh-cn/ 9 | en/ 10 | source/*.ipynb 11 | a.sh 12 | jupyter.md 13 | 0.0-teston* 14 | exercises/out 15 | out 16 | -------------------------------------------------------------------------------- /1.2-Spinal-design-Rules-error-Check.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Assignement overlap\n", 33 | "SpinalHDL will check that no signal assignement completly erases a previous one. \n", 34 | "The following code will throw the following error:" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "class TopLevel extends Component {\n", 44 | " val a = UInt(8 bits)\n", 45 | " a := 42\n", 46 | " a := 66 //Erease the a := 42 :(\n", 47 | "}\n", 48 | "showRtl(new TopLevel)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "A fix could be:" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "class TopLevel extends Component {\n", 65 | " val a = UInt(8 bits)\n", 66 | " a := 42\n", 67 | " val something = in Bool()\n", 68 | " when(something){\n", 69 | " a := 66\n", 70 | " }\n", 71 | "}\n", 72 | "showRtl(new TopLevel)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "But in the case you really want to override the previous assignement (Yes, it could make sense in some cases), you can do the following:" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "class TopLevel extends Component {\n", 89 | " val a = UInt(8 bits)\n", 90 | " a := 42\n", 91 | " a.allowOverride\n", 92 | " a := 66\n", 93 | "}\n", 94 | "showRtl(new TopLevel)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## Clock crossing violation\n", 102 | "SpinalHDL will check that every register of your design only depends (through some combinatorial logic) on registers which use the same or a synchronous clock domain. \n", 103 | "\n", 104 | "The following code will throw error:" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "class TopLevel extends Component {\n", 114 | " val clkA = ClockDomain.external(\"clkA\")\n", 115 | " val clkB = ClockDomain.external(\"clkB\")\n", 116 | "\n", 117 | " val regA = clkA(Reg(UInt(8 bits))) //PlayDev.scala:834\n", 118 | " val regB = clkB(Reg(UInt(8 bits))) //PlayDev.scala:835\n", 119 | "\n", 120 | " val tmp = regA + regA //PlayDev.scala:838\n", 121 | " regB := tmp\n", 122 | "}\n", 123 | "showRtl(new TopLevel)" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "There are multiple possible fixes:\n", 131 | "### crossClockDomain tag\n", 132 | "The crossClockDomain tag can be used to say “It’s alright, don’t panic” to SpinalHDL" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "class TopLevel extends Component {\n", 142 | " val clkA = ClockDomain.external(\"clkA\")\n", 143 | " val clkB = ClockDomain.external(\"clkB\")\n", 144 | "\n", 145 | " val regA = clkA(Reg(UInt(8 bits)))\n", 146 | " val regB = clkB(Reg(UInt(8 bits))).addTag(crossClockDomain)\n", 147 | "\n", 148 | "\n", 149 | " val tmp = regA + regA\n", 150 | " regB := tmp\n", 151 | "}\n", 152 | "showRtl(new TopLevel)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "### setSyncronousWith\n", 160 | "You can also specify that two clock domains are syncronous together." 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "class TopLevel extends Component {\n", 170 | " val clkA = ClockDomain.external(\"clkA\")\n", 171 | " val clkB = ClockDomain.external(\"clkB\")\n", 172 | " clkB.setSyncronousWith(clkA)\n", 173 | "\n", 174 | " val regA = clkA(Reg(UInt(8 bits)))\n", 175 | " val regB = clkB(Reg(UInt(8 bits)))\n", 176 | "\n", 177 | "\n", 178 | " val tmp = regA + regA\n", 179 | " regB := tmp\n", 180 | "}\n", 181 | "\n", 182 | "showRtl(new TopLevel)" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "### BufferCC\n", 190 | "Signal Bits or Gray-coded Bits can use BufferCC to cross different clockDomain" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "class syncRead2Write extends Component {\n", 200 | " val io = new Bundle{\n", 201 | " val pushClock, pushRst = in Bool()\n", 202 | " val readPtr = in UInt(8 bits)\n", 203 | " }\n", 204 | " val pushCC = new ClockingArea(ClockDomain(io.pushClock, io.pushRst)) {\n", 205 | " val pushPtrGray = RegNext(toGray(io.readPtr)) init(0)\n", 206 | " }\n", 207 | "}\n", 208 | "showRtl(new syncRead2Write)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## Combinational loop\n", 223 | "SpinalHDL will check that there are no combinatorial loops in the design.\n", 224 | "\n", 225 | "The following code will throw error:" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "class TopLevel extends Component {\n", 235 | " val a = UInt(8 bits) //PlayDev.scala line 831\n", 236 | " val b = UInt(8 bits) //PlayDev.scala line 832\n", 237 | " val c = UInt(8 bits)\n", 238 | " val d = UInt(8 bits)\n", 239 | "\n", 240 | " a := b\n", 241 | " b := c | d\n", 242 | " d := a\n", 243 | " c := 0\n", 244 | "}\n", 245 | "showRtl(new TopLevel)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "A possible fix could be:" 253 | ] 254 | }, 255 | { 256 | "cell_type": "code", 257 | "execution_count": null, 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [ 261 | "class TopLevel extends Component {\n", 262 | " val a = UInt(8 bits) //PlayDev.scala line 831\n", 263 | " val b = UInt(8 bits) //PlayDev.scala line 832\n", 264 | " val c = UInt(8 bits)\n", 265 | " val d = UInt(8 bits)\n", 266 | "\n", 267 | " a := b\n", 268 | " b := c | d\n", 269 | " d := 42\n", 270 | " c := 0\n", 271 | "}\n", 272 | "showRtl(new TopLevel)" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "### False-positive\n", 280 | "It should be said that SpinalHDL’s algorithm to detect combinatorial loops can be pessimistic, and it may give false positives. If it is giving a false positive, you can manualy disable loop checking on one signal of the loop like so:" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "class TopLevel extends Component {\n", 290 | " val a = UInt(8 bits)\n", 291 | " a := 0\n", 292 | " a(1) := a(0) //False positive because of this line\n", 293 | "}\n", 294 | "showRtl(new TopLevel)" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "could be fixed by :" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": null, 307 | "metadata": {}, 308 | "outputs": [], 309 | "source": [ 310 | "class TopLevel extends Component {\n", 311 | " val a = UInt(8 bits).noCombLoopCheck\n", 312 | " a := 0\n", 313 | " a(1) := a(0)\n", 314 | "}\n", 315 | "showRtl(new TopLevel)" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [ 322 | "It should also be said that assignments such as (a(1) := a(0)) can make some tools like Verilator unhappy. It may be better to use a Vec(Bool(), 8) in this case" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "## Hierarchy violation\n", 330 | "\n", 331 | "SpinalHDL will check that signals are never accessed outside of the current component’s scope.\n", 332 | "\n", 333 | "The following signals can be read inside a component:\n", 334 | "\n", 335 | "- All directionless signals defined in the current component\n", 336 | "\n", 337 | "- All in/out/inout signals of the current component\n", 338 | "\n", 339 | "- All in/out/inout signals of children components\n", 340 | "\n", 341 | "In addition, the following signals can be assigned to inside a component:\n", 342 | "\n", 343 | "- All directionless signals defined in the current component\n", 344 | "\n", 345 | "- All out/inout signals of the current component\n", 346 | "\n", 347 | "- All in/inout signals of children components\n", 348 | "\n", 349 | "If a `HIERARCHY VIOLATION`error appears, it means that one of the above rules was violated.\n", 350 | "\n", 351 | "The following code will throw error:" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": null, 357 | "metadata": {}, 358 | "outputs": [], 359 | "source": [ 360 | "class TopLevel extends Component {\n", 361 | " val io = new Bundle {\n", 362 | " val a = in UInt(8 bits)\n", 363 | " }\n", 364 | " val tmp = U\"x42\"\n", 365 | " io.a := tmp\n", 366 | "}\n", 367 | "showRtl(new TopLevel)" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "metadata": {}, 373 | "source": [ 374 | "A fix could be :" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "metadata": {}, 381 | "outputs": [], 382 | "source": [ 383 | "class TopLevel extends Component {\n", 384 | " val io = new Bundle {\n", 385 | " val a = out UInt(8 bits) // changed from in to out\n", 386 | " }\n", 387 | " val tmp = U\"x42\"\n", 388 | " io.a := tmp\n", 389 | "}\n", 390 | "showRtl(new TopLevel)" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": {}, 396 | "source": [ 397 | "## Io bundle\n", 398 | "SpinalHDL will check that each io bundle contains only in/out/inout signals." 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "metadata": {}, 405 | "outputs": [], 406 | "source": [ 407 | "class TopLevel extends Component {\n", 408 | " val io = new Bundle {\n", 409 | " val a = UInt(8 bits)\n", 410 | " }\n", 411 | "}\n", 412 | "showRtl(new TopLevel)" 413 | ] 414 | } 415 | ], 416 | "metadata": { 417 | "kernelspec": { 418 | "display_name": "Scala", 419 | "language": "scala", 420 | "name": "scala" 421 | }, 422 | "language_info": { 423 | "codemirror_mode": "text/x-scala", 424 | "file_extension": ".scala", 425 | "mimetype": "text/x-scala", 426 | "name": "scala", 427 | "nbconvert_exporter": "script", 428 | "version": "2.11.12" 429 | }, 430 | "toc": { 431 | "base_numbering": 1, 432 | "nav_menu": {}, 433 | "number_sections": true, 434 | "sideBar": true, 435 | "skip_h1_title": false, 436 | "title_cell": "Table of Contents", 437 | "title_sidebar": "Contents", 438 | "toc_cell": false, 439 | "toc_position": {}, 440 | "toc_section_display": true, 441 | "toc_window_display": false 442 | } 443 | }, 444 | "nbformat": 4, 445 | "nbformat_minor": 2 446 | } 447 | -------------------------------------------------------------------------------- /2.5-Spinal-core-Width-propogate.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load Spinal HDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Width propogate" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "### undefined width propogate" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "class T1 extends Component{\n", 49 | " val a = in UInt(8 bits)\n", 50 | " val b = out UInt()\n", 51 | " b := a\n", 52 | "}" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "### unsigned auto resize" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "class T2 extends Component{\n", 69 | " val a = in UInt(8 bits)\n", 70 | " val b = out UInt(10 bits)\n", 71 | " b := a.resized\n", 72 | "}" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "### signed auto resize" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "class T3 extends Component{\n", 89 | " val a = in SInt(8 bits)\n", 90 | " val b = out SInt( )\n", 91 | " b := a.resize(16)\n", 92 | "}\n", 93 | "showRtl(new T3)" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "### width auto cut" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "class T4 extends Component{\n", 110 | " val a = in SInt(8 bits)\n", 111 | " val b = out SInt(4 bits)\n", 112 | " b := a.resized\n", 113 | "}\n", 114 | "showRtl(new T4)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "class T4 extends Component{\n", 124 | " val a = in SInt(8 bits)\n", 125 | " val b = out Bool()\n", 126 | " b := a.andR\n", 127 | "}\n", 128 | "showRtl(new T4)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "## Width propogate cross boundary" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "class Pass extends Component{\n", 145 | " val a = in UInt() //without width declare\n", 146 | " val b = out UInt() //without width declare\n", 147 | " b := a\n", 148 | "}\n", 149 | "\n", 150 | "class Top extends Component{\n", 151 | " val x = in UInt(8 bits)\n", 152 | " val y = out UInt() //without width declare\n", 153 | " \n", 154 | " val uut = new Pass\n", 155 | " uut.a := x\n", 156 | " y := uut.b\n", 157 | "}\n", 158 | "showRtl(new Top)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "## Bit Join\n", 166 | "`##` return Bits" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "class T5 extends Component{\n", 176 | " val a0 = in SInt(8 bits) \n", 177 | " val a1 = in SInt(8 bits)\n", 178 | " val c = out SInt()\n", 179 | " c:= (a0 ## a1).asSInt\n", 180 | "}\n", 181 | "showRtl(new T5)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "## Bits slice" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": { 195 | "scrolled": true 196 | }, 197 | "outputs": [], 198 | "source": [ 199 | "class T6 extends Component{\n", 200 | " val a = in SInt(16 bits) \n", 201 | " val b = out SInt()\n", 202 | " val c = out SInt() \n", 203 | " val o0,o1,o2,o3 = out Bool()\n", 204 | " \n", 205 | " b := a(5 downto 2) \n", 206 | " c := a(0 to 4)\n", 207 | " o0 := a.msb\n", 208 | " o1 := a.lsb\n", 209 | " o2 := a(0)\n", 210 | " o3 := a(9)\n", 211 | " \n", 212 | " println(c.getWidth)\n", 213 | "}\n", 214 | "\n", 215 | "showRtl(new T6)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [] 224 | } 225 | ], 226 | "metadata": { 227 | "kernelspec": { 228 | "display_name": "Scala", 229 | "language": "scala", 230 | "name": "scala" 231 | }, 232 | "language_info": { 233 | "codemirror_mode": "text/x-scala", 234 | "file_extension": ".scala", 235 | "mimetype": "text/x-scala", 236 | "name": "scala", 237 | "nbconvert_exporter": "script", 238 | "version": "2.12.8" 239 | }, 240 | "toc": { 241 | "base_numbering": 1, 242 | "nav_menu": {}, 243 | "number_sections": true, 244 | "sideBar": true, 245 | "skip_h1_title": false, 246 | "title_cell": "Table of Contents", 247 | "title_sidebar": "Contents", 248 | "toc_cell": false, 249 | "toc_position": {}, 250 | "toc_section_display": true, 251 | "toc_window_display": false 252 | } 253 | }, 254 | "nbformat": 4, 255 | "nbformat_minor": 2 256 | } 257 | -------------------------------------------------------------------------------- /3.3-Spinal-lib-Regif.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote." 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## RegIf(Rigister Bank Solution)\n", 33 | " **$\\color{#FF3030}{Note}$** This feature only available on Spinal1.4.1 and higher\n", 34 | "### Creat Reg" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "import spinal.lib.bus.amba3.apb._\n", 44 | "import spinal.lib.bus.regif._\n", 45 | "import spinal.lib.bus.regif.Document.{CHeaderGenerator, HtmlGenerator}\n", 46 | "\n", 47 | "class RegBankExample extends Component{\n", 48 | " val io = new Bundle{\n", 49 | " val apb = slave(Apb3(Apb3Config(16,32)))\n", 50 | " }\n", 51 | " val busif = BusInterface(io.apb,(0x0000, 100 Byte), 0)\n", 52 | " val M_REG0 = busif.newReg(doc=\"REG0\")\n", 53 | " val M_REG1 = busif.newReg(doc=\"REG1\")\n", 54 | " val M_REG2 = busif.newReg(doc=\"REG2\")\n", 55 | "\n", 56 | " val M_REGn = busif.newRegAt(address=0x40, doc=\"REGn\")\n", 57 | " val M_REGn1 = busif.newReg(doc=\"REGn1\")\n", 58 | " busif.accept(HtmlGenerator(\"RegBankExample.html\", name = \"RegBank\"))\n", 59 | " busif.accept(CHeaderGenerator(\"RegBankExample.h\", prefix = \"RB\"))\n", 60 | " }\n", 61 | "showRtl(new RegBankExample)" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "**it will automatic generate 3 file**\n", 69 | "\n", 70 | "- `./rtl/RegBankExample.v`\n", 71 | "- `./rtl/RegBankExample.html`, open with web-browser and take a look**\n", 72 | "- `./rtl/RegBankExample.h`, auto generator c Head file" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "![B](./source/a.gif)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "### Creat Field\n", 87 | "\n", 88 | " register field suport 28 AccessType \n", 89 | " \n", 90 | " Most of these come from UVM\n", 91 | "\n", 92 | "| AccessType | Description | Status |\n", 93 | "|------------|---------------------------------------------------------------------------------|--------|\n", 94 | "| RO | w: no effect, r: no effect | UVM |\n", 95 | "| RW | w: as-is, r: no effect | UVM |\n", 96 | "| RC | w: no effect, r: clears all bits | UVM |\n", 97 | "| RS | w: no effect, r: sets all bits | UVM |\n", 98 | "| WRC | w: as-is, r: clears all bits | UVM |\n", 99 | "| WRS | w: as-is, r: sets all bits | UVM |\n", 100 | "| WC | w: clears all bits, r: no effect | UVM |\n", 101 | "| WS | w: sets all bits, r: no effect | UVM |\n", 102 | "| WSRC | w: sets all bits, r: clears all bits | UVM |\n", 103 | "| WCRS | w: clears all bits, r: sets all bits | UVM |\n", 104 | "| W1C | w: 1/0 clears/no effect on matching bit, r: no effect | UVM |\n", 105 | "| W1S | w: 1/0 sets/no effect on matching bit, r: no effect | UVM |\n", 106 | "| W1T | w: 1/0 toggles/no effect on matching bit, r: no effect | UVM |\n", 107 | "| W0C | w: 1/0 no effect on/clears matching bit, r: no effect | UVM |\n", 108 | "| W0S | w: 1/0 no effect on/sets matching bit, r: no effect | UVM |\n", 109 | "| W0T | w: 1/0 no effect on/toggles matching bit, r: no effect | UVM |\n", 110 | "| W1SRC | w: 1/0 sets/no effect on matching bit, r: clears all bits | UVM |\n", 111 | "| W1CRS | w: 1/0 clears/no effect on matching bit, r: sets all bits | UVM |\n", 112 | "| W0SRC | w: 1/0 no effect on/sets matching bit, r: clears all bits | UVM |\n", 113 | "| W0CRS | w: 1/0 no effect on/clears matching bit, r: sets all bits | UVM |\n", 114 | "| WO | w: as-is, r: error | UVM |\n", 115 | "| WOC | w: clears all bits, r: error | UVM |\n", 116 | "| WOS | w: sets all bits, r: error | UVM |\n", 117 | "| W1 | w: first one after ~hard~ reset is as-is, other w have no effects, r: no effect | UVM |\n", 118 | "| WO1 | w: first one after ~hard~ reset is as-is, other w have no effects, r: error | UVM |\n", 119 | "| NA | w: reserved, r: reserved | New |\n", 120 | "| W1P | w: 1/0 pulse/no effect on matching bit, r: no effect | New |\n", 121 | "| W0P | w: 0/1 pulse/no effect on matching bit, r: no effect | New |\n", 122 | "\n", 123 | "Automatic field allocate" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "import spinal.lib.bus.amba3.apb._\n", 133 | "import spinal.lib.bus.regif._\n", 134 | "import spinal.lib.bus.regif.AccessType._\n", 135 | "\n", 136 | "class RegBankExample extends Component{\n", 137 | " val io = new Bundle{\n", 138 | " val apb = slave(Apb3(Apb3Config(16,32)))\n", 139 | " }\n", 140 | " val busif = BusInterface(io.apb,(0x0000, 100 Byte), 0)\n", 141 | " val M_REG0 = busif.newReg(doc=\"REG1\")\n", 142 | " val fd0 = M_REG0.field(2 bits, RW, doc= \"fields 0\")\n", 143 | " M_REG0.reserved(5 bits)\n", 144 | " val fd1 = M_REG0.field(3 bits, RW, doc= \"fields 0\")\n", 145 | " val fd2 = M_REG0.field(3 bits, RW, doc= \"fields 0\")\n", 146 | " //auto reserved 2 bits\n", 147 | " val fd3 = M_REG0.fieldAt(pos=16, 4 bits, RC, doc= \"fields 3\")\n", 148 | " //auto reserved 12 bits\n", 149 | " busif.accept(HtmlGenerator(\"RegBankExample.html\", name = \"RegBank\")) \n", 150 | " //busif.accept(CHeaderGenerator(\"RegBankExample.h\", prefix = \"RB\"))\n", 151 | "}\n", 152 | "showRtl(new RegBankExample) " 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "![B](./source/b.gif)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "### confilict detection" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "import spinal.lib.bus.amba3.apb._\n", 176 | "import spinal.lib.bus.regif._\n", 177 | "import spinal.lib.bus.regif.AccessType._\n", 178 | "\n", 179 | "class RegBankExample extends Component{\n", 180 | " val io = new Bundle{\n", 181 | " val apb = slave(Apb3(Apb3Config(16,32)))\n", 182 | " }\n", 183 | " val busSlave = BusInterface(io.apb,(0x0000, 100 Byte), 0)\n", 184 | " val M_REG1 = busSlave.newReg(doc=\"REG1\")\n", 185 | " val r1fd0 = M_REG1.field(16 bits, RW, doc=\"fields 0\")\n", 186 | " val r1fd2 = M_REG1.field(16 bits, RW, doc=\"fields 1\")\n", 187 | "}\n", 188 | "showRtl(new RegBankExample) " 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "import spinal.lib.bus.amba3.apb._\n", 198 | "import spinal.lib.bus.regif._\n", 199 | "import spinal.lib.bus.regif.AccessType._\n", 200 | "\n", 201 | "class RegBankExample extends Component{\n", 202 | " val io = new Bundle{\n", 203 | " val apb = slave(Apb3(Apb3Config(16,32)))\n", 204 | " }\n", 205 | " val busSlave = BusInterface(io.apb,(0x0000, 100 Byte),0 )\n", 206 | " val M_REG1 = busSlave.newReg(doc=\"REG1\")\n", 207 | " val r1fd0 = M_REG1.field(16 bits, RW, doc=\"fields 0\")\n", 208 | " val r1fd2 = M_REG1.fieldAt(pos =16, 2 bits, RW, doc=\"fields 1\")\n", 209 | "}\n", 210 | "showRtl(new RegBankExample) " 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "### Document \n", 218 | "BusInterface will auto generate Register document, default generate HTML type.\n", 219 | "![B](./source/regif-gen-doc.png)\n", 220 | "```scala\n", 221 | "class RegBankExample extends Component{\n", 222 | " val io = new Bundle{\n", 223 | " apb = Apb3(Apb3Config(16,32))\n", 224 | " }\n", 225 | " val busSlave = BusInterface(io.apb,(0x0000, 100 Byte)\n", 226 | " val M_REG1 = busSlave.newReg(doc=\"REG1\")\n", 227 | " ...\n", 228 | "}\n", 229 | "```\n", 230 | "will auto generate RegBankExample.html under rtl dir,\n", 231 | "you can also generate manully \n", 232 | "```scala\n", 233 | "busSlave.document(\"RegIf.html\")\n", 234 | "```" 235 | ] 236 | }, 237 | { 238 | "cell_type": "markdown", 239 | "metadata": {}, 240 | "source": [ 241 | "### Interrupt Example\n", 242 | "\n", 243 | "a interrupt Example, there three triggers tx_done,rx_done,frame_end\n", 244 | "\n", 245 | "You need manually add interrupt Enable/Mask/Status register interface like this" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "import spinal.lib.bus.regif.Document.{CHeaderGenerator, HtmlGenerator}\n", 255 | "class cpInterruptExample extends Component {\n", 256 | " val io = new Bundle {\n", 257 | " val tx_done, rx_done, frame_end = in Bool()\n", 258 | " val interrupt = out Bool()\n", 259 | " val apb = slave(Apb3(Apb3Config(16, 32)))\n", 260 | " }\n", 261 | " val busif = Apb3BusInterface(io.apb, (0x000, 100 Byte))\n", 262 | "\n", 263 | " val M_CP_INT_EN = busif.newReg(doc=\"cp int enable register\")\n", 264 | " val tx_int_en = M_CP_INT_EN.field(1 bits, RW, doc=\"tx interrupt enable register\")\n", 265 | " val rx_int_en = M_CP_INT_EN.field(1 bits, RW, doc=\"rx interrupt enable register\")\n", 266 | " val frame_int_en = M_CP_INT_EN.field(1 bits, RW, doc=\"frame interrupt enable register\")\n", 267 | " val M_CP_INT_MASK = busif.newReg(doc=\"cp int mask register\")\n", 268 | " val tx_int_mask = M_CP_INT_MASK.field(1 bits, RW, doc=\"tx interrupt mask register\")\n", 269 | " val rx_int_mask = M_CP_INT_MASK.field(1 bits, RW, doc=\"rx interrupt mask register\")\n", 270 | " val frame_int_mask = M_CP_INT_MASK.field(1 bits, RW, doc=\"frame interrupt mask register\")\n", 271 | " val M_CP_INT_STATE = busif.newReg(doc=\"cp int state register\")\n", 272 | " val tx_int_state = M_CP_INT_STATE.field(1 bits, RW, doc=\"tx interrupt state register\")\n", 273 | " val rx_int_state = M_CP_INT_STATE.field(1 bits, RW, doc=\"rx interrupt state register\")\n", 274 | " val frame_int_state = M_CP_INT_STATE.field(1 bits, RW, doc=\"frame interrupt state register\")\n", 275 | "\n", 276 | " when(io.rx_done && rx_int_en(0)){tx_int_state(0).set()}\n", 277 | " when(io.tx_done && tx_int_en(0)){tx_int_state(0).set()}\n", 278 | " when(io.frame_end && frame_int_en(0)){tx_int_state(0).set()}\n", 279 | "\n", 280 | " io.interrupt := (tx_int_mask(0) && tx_int_state(0) ||\n", 281 | " rx_int_mask(0) && rx_int_state(0) ||\n", 282 | " frame_int_mask(0) && frame_int_state(0))\n", 283 | " busif.accept(HtmlGenerator(\"Intr.html\", name = \"Intr\"))\n", 284 | " busif.accept(CHeaderGenerator(\"Intr.h\", prefix = \"Intr\")) \n", 285 | "}\n", 286 | "showRtl(new cpInterruptExample)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "You can find this is a very tedious and repetitive work, \n", 294 | "a better way is creat a Factory fucntion by Macros auto complet those work instead manully creat them.\n", 295 | "```scala\n", 296 | "io.interrupt := interruptFactory2(busif,\"M_CP\", io.tx_done,io.rx_done,io.frame_end)\n", 297 | "```\n", 298 | "![B](./source/regif-gen-int.png)\n", 299 | "This is much more convenient than manually do that" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": null, 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [ 308 | "class cpInterruptFactoryExample extends Component {\n", 309 | " val io = new Bundle {\n", 310 | " val tx_done, rx_done, frame_end = in Bool()\n", 311 | " val interrupt = out Bool()\n", 312 | " val apb = slave(Apb3(Apb3Config(16, 32)))\n", 313 | " }\n", 314 | " val busif2 = Apb3BusInterface(io.apb, (0x000, 100 Byte))\n", 315 | "\n", 316 | " val tx = io.tx_done\n", 317 | " val rx = io.rx_done\n", 318 | " val frame = io.frame_end\n", 319 | "\n", 320 | " io.interrupt := InterruptFactory(busif2,\"M_CP\",tx,rx,frame)\n", 321 | " \n", 322 | " busif2.accept(HtmlGenerator(\"IntrFact.html\", name = \"Intr\"))\n", 323 | " busif2.accept(CHeaderGenerator(\"IntrFact.h\", prefix = \"Intr\")) \n", 324 | "}\n", 325 | "showRtl(new cpInterruptFactoryExample)" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": {}, 331 | "source": [ 332 | " " 333 | ] 334 | } 335 | ], 336 | "metadata": { 337 | "kernelspec": { 338 | "display_name": "Scala", 339 | "language": "scala", 340 | "name": "scala" 341 | }, 342 | "language_info": { 343 | "codemirror_mode": "text/x-scala", 344 | "file_extension": ".scala", 345 | "mimetype": "text/x-scala", 346 | "name": "scala", 347 | "nbconvert_exporter": "script", 348 | "version": "2.11.12" 349 | } 350 | }, 351 | "nbformat": 4, 352 | "nbformat_minor": 2 353 | } 354 | -------------------------------------------------------------------------------- /3.4-Spinal-lib-Soc.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Chapter\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [] 41 | } 42 | ], 43 | "metadata": { 44 | "kernelspec": { 45 | "display_name": "Scala", 46 | "language": "scala", 47 | "name": "scala" 48 | }, 49 | "language_info": { 50 | "codemirror_mode": "text/x-scala", 51 | "file_extension": ".scala", 52 | "mimetype": "text/x-scala", 53 | "name": "scala", 54 | "nbconvert_exporter": "script", 55 | "version": "2.12.8" 56 | }, 57 | "toc": { 58 | "base_numbering": 1, 59 | "nav_menu": {}, 60 | "number_sections": true, 61 | "sideBar": true, 62 | "skip_h1_title": false, 63 | "title_cell": "Table of Contents", 64 | "title_sidebar": "Contents", 65 | "toc_cell": false, 66 | "toc_position": {}, 67 | "toc_section_display": true, 68 | "toc_window_display": false 69 | } 70 | }, 71 | "nbformat": 4, 72 | "nbformat_minor": 2 73 | } 74 | -------------------------------------------------------------------------------- /3.5-Spinal-lib-VexRiscv.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Chapter\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [] 41 | } 42 | ], 43 | "metadata": { 44 | "kernelspec": { 45 | "display_name": "Scala", 46 | "language": "scala", 47 | "name": "scala" 48 | }, 49 | "language_info": { 50 | "codemirror_mode": "text/x-scala", 51 | "file_extension": ".scala", 52 | "mimetype": "text/x-scala", 53 | "name": "scala", 54 | "nbconvert_exporter": "script", 55 | "version": "2.12.8" 56 | }, 57 | "toc": { 58 | "base_numbering": 1, 59 | "nav_menu": {}, 60 | "number_sections": true, 61 | "sideBar": true, 62 | "skip_h1_title": false, 63 | "title_cell": "Table of Contents", 64 | "title_sidebar": "Contents", 65 | "toc_cell": false, 66 | "toc_position": {}, 67 | "toc_section_display": true, 68 | "toc_window_display": false 69 | } 70 | }, 71 | "nbformat": 4, 72 | "nbformat_minor": 2 73 | } 74 | -------------------------------------------------------------------------------- /3.6-Spinal-memWrapper.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Chapter\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "class spRAM(aw: Int, dw: Int, x: Vendor, memName: String = \"\") extends Component with MemWrap{\n", 42 | " val io = new Bundle{\n", 43 | " val cs, wr = in Bool()\n", 44 | " val addr = in UInt(aw bits)\n", 45 | " val wdata = in Bits(dw bits)\n", 46 | " val rdata = out Bits(dw bits)\n", 47 | " }\n", 48 | " noIoPrefix() \n", 49 | " val ram = new bf_rfsp(mc, memName)\n", 50 | " ram.Build(this)\n", 51 | "}\n", 52 | "\n", 53 | "trait Vendor{\n", 54 | " def rfspMem(mc: MemConfig): MemBlackBox = {new rfsp(mc)} \n", 55 | " def rf2pMem(mc: MemConfig): MemBlackBox = {new rfsp(mc)} \n", 56 | " def sfdpMem(mc: MemConfig): MemBlackBox = {new rfsp(mc)}\n", 57 | "}\n", 58 | "\n", 59 | "object BigFish extends Vendor{\n", 60 | " override def genMemWrapper(mc: MemConfig): MemBlackBox = {new rfsp(mc)}\n", 61 | "}\n", 62 | "\n", 63 | "object SPRD extends Vendor{\n", 64 | " override def genMemWrapper(mc: MemConfig): MemBlackBox = {new rfsp(mc)}\n", 65 | "}\n", 66 | "\n", 67 | "object ZTE extends Vendor{\n", 68 | " override def genMemWrapper(mc: MemConfig): MemBlackBox = {new rfsp(mc)} \n", 69 | " override def genMemWrapper(mc: MemConfig): MemBlackBox = {new rfsp(mc)}\n", 70 | "}" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "case class MemConfig(dw: Int,\n", 80 | " depth: Int,\n", 81 | " maskBitWidth: Int = 0,\n", 82 | " withScan: Boolean = false,\n", 83 | " withBist: Boolean = false,\n", 84 | " withRepaire: Boolean = false){\n", 85 | " def bistaw: Int = 14\n", 86 | " def aw: Int = log2Up(depth)\n", 87 | " def maskName: String = maskBitWidth match {\n", 88 | " case 16 => \"wordmask\"\n", 89 | " case 8 => \"bytemask\"\n", 90 | " case 1 => \"bitmask\"\n", 91 | " case 0 => \"nomask\"\n", 92 | " case _ => SpinalError(\"Undefiend BWE type\")\n", 93 | " }\n", 94 | " def bweWidth = if(maskBitWidth == 0) 0 else math.ceil(dw.toDouble/maskBitWidth).toInt\n", 95 | " def nonbwe: Boolean = (maskBitWidth == 0)\n", 96 | " def needBWE: Boolean = !nonbwe\n", 97 | " def genBWE(): Bits = in Bits(bweWidth bits)\n", 98 | "}\n", 99 | "\n", 100 | "class spRAM(mc: MemConfig, memName: String = \"\") extends Component with MemWrap{\n", 101 | " val io = new Bundle{\n", 102 | " val cs, wr = in Bool()\n", 103 | " val bwe = mc.genBWE()\n", 104 | " val addr = in UInt(mc.aw bits)\n", 105 | " val wdata = in Bits(mc.dw bits)\n", 106 | " val rdata = out Bits(mc.dw bits)\n", 107 | " }\n", 108 | " // Map the clk\n", 109 | " // mapCurrentClockDomain(io.clk)\n", 110 | " // Remove io_ prefix\n", 111 | " noIoPrefix()\n", 112 | "// val ram = new ram_1rw(mc, memName)\n", 113 | "// val ram = new vendor.tsmc.mem_1rw(mc, memName)\n", 114 | " val ram = new bf_rfsp(mc, memName)\n", 115 | " ram.Build(this)\n", 116 | "}" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "\n", 126 | "class bf_rfsp(mc: MemConfig, memName: String = \"\") extends MemBlackBox{\n", 127 | " /*Dirty Temporary solution for deadline, TODO*/\n", 128 | " \n", 129 | " this.setDefinitionName(\"Mwrapper_srsp${mc.depth}x${mc.dw}m16b4w1r\")\n", 130 | "\n", 131 | " val io = new Bundle { iobd =>\n", 132 | " val CLK = in Bool()\n", 133 | " val A = in UInt(mc.aw bits)\n", 134 | " val D = in Bits(mc.dw bits)\n", 135 | " val CEN = in Bool()\n", 136 | " val WEN = in Bool()\n", 137 | " val Q = out Bits(mc.dw bits)\n", 138 | "\n", 139 | " val cd = ClockDomain(io.CLK)\n", 140 | "\n", 141 | " def Build(wrap: spRAM) = {\n", 142 | " wrap.clockDomain.setSyncWith(cd)\n", 143 | " this.io.CLK := wrap.clockDomain.readClockWire\n", 144 | " this.io.A := wrap.io.addr\n", 145 | " this.io.CEN := ~wrap.io.cs\n", 146 | " this.io.WEN := ~wrap.io.wr\n", 147 | " this.io.D := wrap.io.wdata\n", 148 | " wrap.io.rdata := this.io.Q\n", 149 | " }\n", 150 | " noIoPrefix()\n", 151 | "// mapClockDomain(clock = io.CLK)\n", 152 | "\n", 153 | " val mem = new Mem(Bits(mc.dw bits), mc.depth)\n", 154 | " \n", 155 | " val ca = new ClockingArea(cd) {\n", 156 | " mem.write(\n", 157 | " address = io.A,\n", 158 | " data = io.D,\n", 159 | " enable = ~io.WEN\n", 160 | " )\n", 161 | "\n", 162 | " io.Q := mem.readSync(\n", 163 | " address = io.A,\n", 164 | " enable = (!io.CEN && io.WEN)\n", 165 | " )\n", 166 | " }\n", 167 | "}\n" 168 | ] 169 | } 170 | ], 171 | "metadata": { 172 | "kernelspec": { 173 | "display_name": "Scala", 174 | "language": "scala", 175 | "name": "scala" 176 | }, 177 | "language_info": { 178 | "codemirror_mode": "text/x-scala", 179 | "file_extension": ".scala", 180 | "mimetype": "text/x-scala", 181 | "name": "scala", 182 | "nbconvert_exporter": "script", 183 | "version": "2.11.12" 184 | }, 185 | "toc": { 186 | "base_numbering": 1, 187 | "nav_menu": {}, 188 | "number_sections": true, 189 | "sideBar": true, 190 | "skip_h1_title": false, 191 | "title_cell": "Table of Contents", 192 | "title_sidebar": "Contents", 193 | "toc_cell": false, 194 | "toc_position": {}, 195 | "toc_section_display": true, 196 | "toc_window_display": false 197 | } 198 | }, 199 | "nbformat": 4, 200 | "nbformat_minor": 2 201 | } 202 | -------------------------------------------------------------------------------- /4.1-Spinal-Example-Simple-ones.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## [APB3 definition](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/apb3.html)\n", 33 | "#### Implementation" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "case class Apb3Config(\n", 43 | " addressWidth : Int,\n", 44 | " dataWidth : Int,\n", 45 | " selWidth : Int = 1,\n", 46 | " useSlaveError : Boolean = true\n", 47 | ")" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "case class Apb3(config: Apb3Config) extends Bundle with IMasterSlave {\n", 57 | " val PADDR = UInt(config.addressWidth bit)\n", 58 | " val PSEL = Bits(config.selWidth bits)\n", 59 | " val PENABLE = Bool()\n", 60 | " val PREADY = Bool()\n", 61 | " val PWRITE = Bool()\n", 62 | " val PWDATA = Bits(config.dataWidth bit)\n", 63 | " val PRDATA = Bits(config.dataWidth bit)\n", 64 | " val PSLVERROR = if(config.useSlaveError) Bool() else null\n", 65 | "\n", 66 | " override def asMaster(): Unit = {\n", 67 | " out(PADDR,PSEL,PENABLE,PWRITE,PWDATA)\n", 68 | " in(PREADY,PRDATA)\n", 69 | " if(config.useSlaveError) in(PSLVERROR)\n", 70 | " }\n", 71 | "}" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "#### Usage" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "val apbConfig = Apb3Config(\n", 88 | " addressWidth = 16,\n", 89 | " dataWidth = 32,\n", 90 | " selWidth = 1,\n", 91 | " useSlaveError = false\n", 92 | ")\n", 93 | "\n", 94 | "val io = new Bundle{\n", 95 | " val apb = slave(Apb3(apbConfig))\n", 96 | "}\n", 97 | "\n", 98 | "io.apb.PREADY := True\n", 99 | "when(io.apb.PSEL(0) && io.apb.PENABLE){\n", 100 | " //...\n", 101 | "}" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## [Carry adder](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/carry_adder.html)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "This example defines a component with inputs `a` and `b`, and a `result` output. At any time, `result` will be the sum of `a` and `b` (combinatorial). This sum is manually done by a carry adder logic." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "class CarryAdder(size : Int) extends Component{\n", 125 | " val io = new Bundle{\n", 126 | " val a = in UInt(size bits)\n", 127 | " val b = in UInt(size bits)\n", 128 | " val result = out UInt(size bits) //result = a + b\n", 129 | " }\n", 130 | "\n", 131 | " var c = False //Carry, like a VHDL variable\n", 132 | " for (i <- 0 until size) { // 0 <= i < size\n", 133 | " //Create some intermediate value in the loop scope.\n", 134 | " val a = io.a(i)\n", 135 | " val b = io.b(i)\n", 136 | "\n", 137 | " //The carry adder's asynchronous logic\n", 138 | " io.result(i) := a ^ b ^ c\n", 139 | " c \\= (a & b) | (a & c) | (b & c); //variable assignment\n", 140 | " }\n", 141 | "}\n", 142 | "\n", 143 | "showRtl(new CarryAdder(4))" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "Note that only variables who declared with `var` support the operator `\\=`." 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## [Color summing](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/color_summing.html)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "case class Color(channelWidth: Int) extends Bundle {\n", 167 | " val r = UInt(channelWidth bits)\n", 168 | " val g = UInt(channelWidth bits)\n", 169 | " val b = UInt(channelWidth bits)\n", 170 | "\n", 171 | " def +(that: Color): Color = {\n", 172 | " val result = Color(channelWidth)\n", 173 | " result.r := this.r + that.r\n", 174 | " result.g := this.g + that.g\n", 175 | " result.b := this.b + that.b\n", 176 | " return result\n", 177 | " }\n", 178 | "\n", 179 | " def clear(): Color ={\n", 180 | " this.r := 0\n", 181 | " this.g := 0\n", 182 | " this.b := 0\n", 183 | " this\n", 184 | " }\n", 185 | "}\n", 186 | "\n", 187 | "class ColorSumming(sourceCount: Int, channelWidth: Int) extends Component {\n", 188 | " val io = new Bundle {\n", 189 | " val sources = in Vec(Color(channelWidth), sourceCount)\n", 190 | " val result = out(Color(channelWidth))\n", 191 | " }\n", 192 | "\n", 193 | " var sum = Color(channelWidth)\n", 194 | " sum.clear()\n", 195 | " for (i <- 0 to sourceCount - 1) {\n", 196 | " sum \\= sum + io.sources(i)\n", 197 | " }\n", 198 | " io.result := sum\n", 199 | "}\n", 200 | "\n", 201 | "showRtl(new ColorSumming(4, 8))" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "## [Counter with clear](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/counter_with_clear.html)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "This example defines a component with a `clear` input and a `value` output. Each clock cycle, the `value` output is incrementing, but when `clear` is high, `value` is cleared." 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "class Counter(width : Int) extends Component{\n", 225 | " val io = new Bundle{\n", 226 | " val clear = in Bool()\n", 227 | " val value = out UInt(width bits)\n", 228 | " }\n", 229 | " val register = Reg(UInt(width bits)) init(0)\n", 230 | " register := register + 1\n", 231 | " when(io.clear){\n", 232 | " register := 0\n", 233 | " }\n", 234 | " io.value := register\n", 235 | "}\n", 236 | "\n", 237 | "showRtl(new Counter(4))" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "## [PLL BlackBox and reset controller](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/pll_resetctrl.html)\n", 245 | "#### The PLL BlackBox definition" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "class PLL extends BlackBox{\n", 255 | " val io = new Bundle{\n", 256 | " val clkIn = in Bool()\n", 257 | " val clkOut = out Bool()\n", 258 | " val isLocked = out Bool()\n", 259 | " }\n", 260 | "\n", 261 | " noIoPrefix()\n", 262 | "}" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "#### TopLevel definition" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [ 278 | "class TopLevel extends Component{\n", 279 | " val io = new Bundle {\n", 280 | " val aReset = in Bool()\n", 281 | " val clk100Mhz = in Bool()\n", 282 | " val result = out UInt(4 bits)\n", 283 | " }\n", 284 | "\n", 285 | " // Create an Area to manage all clocks and reset things\n", 286 | " val clkCtrl = new Area {\n", 287 | " //Instanciate and drive the PLL\n", 288 | " val pll = new PLL\n", 289 | " pll.io.clkIn := io.clk100Mhz\n", 290 | "\n", 291 | " //Create a new clock domain named 'core'\n", 292 | " val coreClockDomain = ClockDomain.internal(\n", 293 | " name = \"core\",\n", 294 | " frequency = FixedFrequency(200 MHz) // This frequency specification can be used\n", 295 | " ) // by coreClockDomain users to do some calculations\n", 296 | "\n", 297 | " //Drive clock and reset signals of the coreClockDomain previously created\n", 298 | " coreClockDomain.clock := pll.io.clkOut\n", 299 | " coreClockDomain.reset := ResetCtrl.asyncAssertSyncDeassert(\n", 300 | " input = io.aReset || ! pll.io.isLocked,\n", 301 | " clockDomain = coreClockDomain\n", 302 | " )\n", 303 | " }\n", 304 | "\n", 305 | " //Create a ClockingArea which will be under the effect of the clkCtrl.coreClockDomain\n", 306 | " val core = new ClockingArea(clkCtrl.coreClockDomain){\n", 307 | " //Do your stuff which use coreClockDomain here\n", 308 | " val counter = Reg(UInt(4 bits)) init(0)\n", 309 | " counter := counter + 1\n", 310 | " io.result := counter\n", 311 | " }\n", 312 | "}\n", 313 | "\n", 314 | "showRtl(new TopLevel)" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "## [RGB to gray](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/rgb_to_gray.html)" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "Let’s imagine a component that converts an RGB color into a gray one, and then writes it into external memory.\n", 329 | "\n", 330 | "| io name | Direction | Description |\n", 331 | "|-|-|-|\n", 332 | "| clear | in | Clear all internal registers |\n", 333 | "| r,g,b | in | Color inputs |\n", 334 | "| wr | out | Memory write |\n", 335 | "| address | out | Memory address, incrementing each cycle |\n", 336 | "| data | out | Memory data, gray level |" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": null, 342 | "metadata": {}, 343 | "outputs": [], 344 | "source": [ 345 | "class RgbToGray extends Component{\n", 346 | " val io = new Bundle{\n", 347 | " val clear = in Bool()\n", 348 | " val r,g,b = in UInt(8 bits)\n", 349 | "\n", 350 | " val wr = out Bool()\n", 351 | " val address = out UInt(16 bits)\n", 352 | " val data = out UInt(8 bits)\n", 353 | " }\n", 354 | "\n", 355 | " def coef(value : UInt,by : Float) : UInt = (value * U((255*by).toInt,8 bits) >> 8)\n", 356 | " val gray = RegNext(\n", 357 | " coef(io.r,0.3f) +\n", 358 | " coef(io.g,0.4f) +\n", 359 | " coef(io.b,0.3f)\n", 360 | " )\n", 361 | "\n", 362 | " val address = CounterFreeRun(stateCount = 1 << 16)\n", 363 | " io.address := address\n", 364 | " io.wr := True\n", 365 | " io.data := gray\n", 366 | "\n", 367 | " when(io.clear){\n", 368 | " gray := 0\n", 369 | " address.clear()\n", 370 | " io.wr := False\n", 371 | " }\n", 372 | "}\n", 373 | "\n", 374 | "showRtl(new RgbToGray)" 375 | ] 376 | }, 377 | { 378 | "cell_type": "markdown", 379 | "metadata": {}, 380 | "source": [ 381 | "## [Sinus rom](https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Examples/Simple%20ones/sinus_rom.html)" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "Let’s imagine that you want to generate a sine wave and also have a filtered version of it (which is completely useless in practice, but let’s do it as an example).\n", 389 | "\n", 390 | "![](./source/sin.svg)" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": {}, 397 | "outputs": [], 398 | "source": [ 399 | "import scala.math\n", 400 | "\n", 401 | "class TopLevel(resolutionWidth: Int, sampleCount: Int) extends Component {\n", 402 | " val io = new Bundle {\n", 403 | " val sin = out SInt(resolutionWidth bits)\n", 404 | " val sinFiltred = out SInt(resolutionWidth bits)\n", 405 | " }\n", 406 | "\n", 407 | " def sinTable = for(sampleIndex <- 0 until sampleCount) yield {\n", 408 | " val sinValue = math.sin(2 * math.Pi * sampleIndex / sampleCount)\n", 409 | " S((sinValue * ((1<> 5) + (io.sin >> 5)) init(0)\n", 418 | "}\n", 419 | "\n", 420 | "showRtl(new TopLevel(24, 256))" 421 | ] 422 | }, 423 | { 424 | "cell_type": "markdown", 425 | "metadata": {}, 426 | "source": [ 427 | "![schematic](./source/sine.png)" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "And then you can find the contents of rom at a [binary file](./rtl/TopLevel.v_toplevel_rom.bin)." 435 | ] 436 | } 437 | ], 438 | "metadata": { 439 | "kernelspec": { 440 | "display_name": "Scala", 441 | "language": "scala", 442 | "name": "scala" 443 | }, 444 | "language_info": { 445 | "codemirror_mode": "text/x-scala", 446 | "file_extension": ".scala", 447 | "mimetype": "text/x-scala", 448 | "name": "scala", 449 | "nbconvert_exporter": "script", 450 | "version": "2.12.11" 451 | } 452 | }, 453 | "nbformat": 4, 454 | "nbformat_minor": 2 455 | } 456 | -------------------------------------------------------------------------------- /4.3-Spinal-Example-Advanced-ones.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Chapter\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [] 41 | } 42 | ], 43 | "metadata": { 44 | "kernelspec": { 45 | "display_name": "Scala", 46 | "language": "scala", 47 | "name": "scala" 48 | }, 49 | "language_info": { 50 | "codemirror_mode": "text/x-scala", 51 | "file_extension": ".scala", 52 | "mimetype": "text/x-scala", 53 | "name": "scala", 54 | "nbconvert_exporter": "script", 55 | "version": "2.12.8" 56 | } 57 | }, 58 | "nbformat": 4, 59 | "nbformat_minor": 2 60 | } 61 | -------------------------------------------------------------------------------- /5.2-Spinal-FAQ.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Bit width inference usage restriction \n", 33 | "Example1:\n", 34 | "```scala\n", 35 | " val c = UInt()\n", 36 | " c.getWidth // X Failed !\n", 37 | " c:= a + b\n", 38 | " c.getWidth // is ok, because c width already inferred by \"c := a + b\"\n", 39 | "```\n", 40 | "Example2:(setAll need getWidth) \n", 41 | "```scala\n", 42 | " val c = out UInt() \n", 43 | " when(clc){c.setAll }.otherwise(c := a * b) //failed\n", 44 | " when(clc){c := a * b }.otherwise(c.setAll) //It's OK\n", 45 | "```" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "class T2 extends Component{\n", 55 | " val a,b = in UInt(8 bits)\n", 56 | " val clc= in Bool()\n", 57 | " val c = out UInt() \n", 58 | "// when(clc){c.setAll }.otherwise(c := a * b) //failed \n", 59 | " when(clc){c := a * b }.otherwise(c.setAll) //It's OK\n", 60 | "}\n", 61 | "showRtl(new T2)" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "## asBool issue\n", 69 | "Please note that for 1 bit data operation, the first method is different from the latter two methods.\n", 70 | "- a.asBits \n", 71 | "- a.lsb \n", 72 | "- a(0) \n", 73 | "```scala\n", 74 | "val a= Bits(1 bits)\n", 75 | "a.asBits //return new Bool,then tmp := a.lsb\n", 76 | "a.lsb //direct operate a(0) bit\n", 77 | "a(0) //direct operate a(0) bit\n", 78 | "```" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "class T1 extends Component{ \n", 88 | " val sel = in Bool()\n", 89 | " val a = Reg(Bits(1 bits)) init 0 \n", 90 | " when(sel){a.asBool.set()} //generate verilog beyond your expectations\n", 91 | "}\n", 92 | "showRtl(new T1) " 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "class T2 extends Component{ \n", 102 | " val sel = in Bool()\n", 103 | " val a = Reg(Bits(1 bits)) init 0 \n", 104 | " when(sel){a(0).set()} \n", 105 | " // when(sel){a.lsb.set()} //also Ok\n", 106 | "}\n", 107 | "showRtl(new T2) " 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "### 4. Overlap or Latch?" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "import scala.collection.mutable.ListBuffer\n", 124 | "class T2 extends Component{\n", 125 | " val a = in UInt(2 bits)\n", 126 | " val Lut = ListBuffer.fill(8)(UInt(8 bits))\n", 127 | " (0 to 7).foreach{ i => \n", 128 | " Lut(i) := i \n", 129 | " }\n", 130 | " switch(a){\n", 131 | " is(0){\n", 132 | " Lut(0) := 3\n", 133 | " Lut(2) := 4\n", 134 | " }\n", 135 | " is(1){\n", 136 | " Lut(3) := 3\n", 137 | " Lut(4) := 4\n", 138 | " }\n", 139 | " is(2){\n", 140 | " Lut(3) := 3\n", 141 | " Lut(4) := 4\n", 142 | " } \n", 143 | " default{\n", 144 | " (0 to 7).foreach{ i => \n", 145 | " Lut(i) := i \n", 146 | " }\n", 147 | " }\n", 148 | " }\n", 149 | "}\n", 150 | "showRtl(new T2) " 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## val def in Trait\n", 158 | "**Attation** val cause Error " 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "trait PRNBase {\n", 168 | " val size: Int \n", 169 | "\n", 170 | " val Mask = (1 << size) - 1 //attation \n", 171 | " val Msb = (1 << (size - 1)) //attation\n", 172 | "}\n", 173 | "\n", 174 | "object GPS extends PRNBase{\n", 175 | " val size = 1023\n", 176 | "}\n", 177 | "\n", 178 | "object BD extends PRNBase{\n", 179 | " val size = 2046\n", 180 | "}\n", 181 | "BD.Mask toHexString // return 0" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "trait PRNBase {\n", 191 | " val size: Int \n", 192 | "\n", 193 | " def Mask = (1 << size) - 1\n", 194 | " def Msb = (1 << (size - 1))\n", 195 | "}\n", 196 | "object BD extends PRNBase{\n", 197 | " val size = 11\n", 198 | "}\n", 199 | "BD.Mask toHexString" 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "## Scala double definition \n", 207 | "```scala \n", 208 | "class TestDoubleDef{\n", 209 | " def foo(p:List[String]) = {}\n", 210 | " def foo(p:List[Int]) = {}\n", 211 | "}\n", 212 | "```\n", 213 | "**raise Error:**\n", 214 | "```sh\n", 215 | "[error] double definition:\n", 216 | "[error] method foo:(List[String])Unit and\n", 217 | "[error] method foo:(List[Int])Unit at line 120\n", 218 | "[error] have same type after erasure: (List)Unit \n", 219 | "```\n", 220 | "Solution: https://stackoverflow.com/questions/3307427/scala-double-definition-2-methods-have-the-same-type-erasure/3544060#3544060" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "metadata": {}, 227 | "outputs": [], 228 | "source": [ 229 | "object MyTransform{\n", 230 | " def apply(x: Int ): Double = x + 0.00 \n", 231 | " def apply(x: List[Int] ): List[Double] = x.map(_+0.00)\n", 232 | " def apply(x: List[Double] ): List[Double] = x.map(_+0.00)\n", 233 | "}" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "**Use implict transform**" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "case class IntList(list: List[Int])\n", 250 | "case class DoubleList(list: List[Double])\n", 251 | "\n", 252 | "implicit def Il(list: List[Int]) = IntList(list)\n", 253 | "implicit def Dl(list: List[Double]) = DoubleList(list)\n", 254 | "\n", 255 | "object FixTo{\n", 256 | " def apply(x: Int ): Double = x + 0.00 \n", 257 | " def apply(x: IntList ): List[Double] = x.list.map(_+0.00)\n", 258 | " def apply(x: DoubleList ): List[Double] = x.list.map(_+0.00)\n", 259 | "}" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "val a = FixTo(3)\n", 269 | "val b = FixTo(DoubleList(List(1,2,3,4,5)))\n", 270 | "val c = FixTo(List(1,2,3,4,5))" 271 | ] 272 | }, 273 | { 274 | "cell_type": "markdown", 275 | "metadata": {}, 276 | "source": [ 277 | "### Watch Dog\n" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "metadata": {}, 284 | "outputs": [], 285 | "source": [ 286 | "class T2 extends Component{ \n", 287 | " val a = out Bits(9 bit)\n", 288 | " a := 133\n", 289 | " class Dog{}\n", 290 | " val xiaogou = new Dog\n", 291 | "\n", 292 | "}\n", 293 | "showRtl(new T2) " 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "class T2 extends Component{ \n", 303 | " class Dog{\n", 304 | " def genTimer(n: Int) = {\n", 305 | " val timer = Reg(UInt(n bits)) init 0 \n", 306 | " val clearTimer = in Bool()\n", 307 | " when(clearTimer){\n", 308 | " timer init 0\n", 309 | " }.otherwise {\n", 310 | " timer := timer + 1\n", 311 | " }\n", 312 | " (clearTimer,timer)\n", 313 | " }\n", 314 | " }\n", 315 | " val xiaogou = new Dog\n", 316 | " val (weigou,timer) = xiaogou.genTimer(8)\n", 317 | "} \n", 318 | "showRtl(new T2) " 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [ 327 | " class Dog {\n", 328 | " def genTimer(n: Int) = {\n", 329 | " val timer = Reg(UInt(n bits)) init 0 \n", 330 | " val clearTimer = in Bool()\n", 331 | " when(clearTimer){\n", 332 | " timer init 0\n", 333 | " }.otherwise {\n", 334 | " timer := timer + 1\n", 335 | " }\n", 336 | " (clearTimer,timer)\n", 337 | " }\n", 338 | " }" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "why not?" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": null, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [ 354 | "class T2 extends Component{ \n", 355 | " val xiaogou = new Dog\n", 356 | " val (weigou,timer) = xiaogou.genTimer(8)\n", 357 | "} \n", 358 | "showRtl(new T2) " 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": null, 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [ 367 | "class T2 extends Component{ \n", 368 | " val (weigou,timer) = (new Dog).genTimer(8)\n", 369 | "} \n", 370 | "showRtl(new T2) " 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "metadata": {}, 376 | "source": [ 377 | "### pretty print" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": {}, 384 | "outputs": [], 385 | "source": [ 386 | "implicit class UtilsExpand(x: HertzNumber) {\n", 387 | " def toString0: String = {\n", 388 | " x.toBigDecimal match {\n", 389 | " case y if y > BigDecimal(1e12) => (y/BigDecimal(1e12)).toDouble + \" THz\"\n", 390 | " case y if y > BigDecimal(1e9) => (y/BigDecimal(1e9)).toDouble + \" GHz\"\n", 391 | " case y if y > BigDecimal(1e6) => (y/BigDecimal(1e6)).toDouble + \" MHz\"\n", 392 | " case y if y > BigDecimal(1e3) => (y/BigDecimal(1e3)).toDouble + \" KHz\"\n", 393 | " case _ => x.toBigDecimal + \"Hz\"\n", 394 | " }\n", 395 | " }\n", 396 | " }\n", 397 | "println((100.21 MHz).toString0)" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": null, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "println(HertzNumber(800010).toString0)" 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": null, 412 | "metadata": {}, 413 | "outputs": [], 414 | "source": [ 415 | "(2 KiB)/7" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": null, 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [ 424 | "2048/7.0" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": null, 430 | "metadata": {}, 431 | "outputs": [], 432 | "source": [ 433 | "implicit class ByteExpand(x: BigInt) {\n", 434 | " def pretty: String = {\n", 435 | " x match {\n", 436 | " case y if y >= (BigInt(1) << 80) => (y/(BigInt(1)<<80)) + \" YiB\"\n", 437 | " case y if y >= (BigInt(1) << 70) => (y/(BigInt(1)<<70)) + \" ZiB\"\n", 438 | " case y if y >= (BigInt(1) << 60) => (y/(BigInt(1)<<60)) + \" EiB\"\n", 439 | " case y if y >= (BigInt(1) << 50) => (y/(BigInt(1)<<50)) + \" PiB\"\n", 440 | " case y if y >= (BigInt(1) << 40) => (y/(BigInt(1)<<40)) + \" TiB\"\n", 441 | " case y if y >= (BigInt(1) << 30) => (y/(BigInt(1)<<30)) + \" GiB\"\n", 442 | " case y if y >= (BigInt(1) << 20) => (y/(BigInt(1)<<20)) + \" MiB\"\n", 443 | " case y if y >= (BigInt(1) << 10) => (y/(BigInt(1)<<10)) + \" KiB\"\n", 444 | " case _ => x + \"Byte\"\n", 445 | " }\n", 446 | " }\n", 447 | " }\n", 448 | "\n", 449 | "println((BigInt(1) << 80 Byte).pretty)" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": null, 455 | "metadata": {}, 456 | "outputs": [], 457 | "source": [ 458 | "20 GiB" 459 | ] 460 | }, 461 | { 462 | "cell_type": "code", 463 | "execution_count": null, 464 | "metadata": {}, 465 | "outputs": [], 466 | "source": [ 467 | "BigDecimal(333.001) toBigInt" 468 | ] 469 | } 470 | ], 471 | "metadata": { 472 | "kernelspec": { 473 | "display_name": "Scala", 474 | "language": "scala", 475 | "name": "scala" 476 | }, 477 | "language_info": { 478 | "codemirror_mode": "text/x-scala", 479 | "file_extension": ".scala", 480 | "mimetype": "text/x-scala", 481 | "name": "scala", 482 | "nbconvert_exporter": "script", 483 | "version": "2.12.8" 484 | }, 485 | "toc": { 486 | "base_numbering": 1, 487 | "nav_menu": {}, 488 | "number_sections": true, 489 | "sideBar": true, 490 | "skip_h1_title": false, 491 | "title_cell": "Table of Contents", 492 | "title_sidebar": "Contents", 493 | "toc_cell": false, 494 | "toc_position": {}, 495 | "toc_section_display": true, 496 | "toc_window_display": false 497 | } 498 | }, 499 | "nbformat": 4, 500 | "nbformat_minor": 2 501 | } 502 | -------------------------------------------------------------------------------- /5.3-Spinal-Utils.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## LattencyAnalysis\n", 33 | "\n", 34 | "```scala\n", 35 | "LatencyAnalysis(dut.io.input, dut.io.output)\n", 36 | "```" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "class T1 extends Component{\n", 46 | " val a,b = in UInt(2 bits)\n", 47 | " val c = RegNext(a)\n", 48 | " val d = RegNext(c*b)\n", 49 | " val e = RegNext(d)\n", 50 | " val f = e + b\n", 51 | " \n", 52 | "println(s\"latency ${LatencyAnalysis(a,c)}\")\n", 53 | "println(s\"latency ${LatencyAnalysis(a,d)}\")\n", 54 | "println(s\"latency ${LatencyAnalysis(a,e)}\")\n", 55 | "println(s\"latency ${LatencyAnalysis(a,f)}\") \n", 56 | "}\n", 57 | "showRtl(new T1)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## OneHot/OneHotMask\n", 65 | "\n", 66 | "- `OHToUInt(bits)`\n", 67 | "- `OHMasking.first(bits)` OneHotMask From Lowerest bit\n", 68 | "- `OHMasking.last(bits)` OneHotMask From Highest bit" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "class OneHot(w: Int) extends Component{\n", 78 | " val bits = in Bits(w bits)\n", 79 | " val int0 = out(OHToUInt(bits))\n", 80 | " val int2 = out(OHToUInt(OHMasking.first(bits)))\n", 81 | " val int3 = out(OHToUInt(OHMasking.last(bits)))\n", 82 | " val int4 = ~int3\n", 83 | " val int7 = U(w-1) - int3\n", 84 | "}" 85 | ] 86 | } 87 | ], 88 | "metadata": { 89 | "kernelspec": { 90 | "display_name": "Scala", 91 | "language": "scala", 92 | "name": "scala" 93 | }, 94 | "language_info": { 95 | "codemirror_mode": "text/x-scala", 96 | "file_extension": ".scala", 97 | "mimetype": "text/x-scala", 98 | "name": "scala", 99 | "nbconvert_exporter": "script", 100 | "version": "2.12.8" 101 | }, 102 | "toc": { 103 | "base_numbering": 1, 104 | "nav_menu": {}, 105 | "number_sections": true, 106 | "sideBar": true, 107 | "skip_h1_title": false, 108 | "title_cell": "Table of Contents", 109 | "title_sidebar": "Contents", 110 | "toc_cell": false, 111 | "toc_position": {}, 112 | "toc_section_display": true, 113 | "toc_window_display": false 114 | } 115 | }, 116 | "nbformat": 4, 117 | "nbformat_minor": 2 118 | } 119 | -------------------------------------------------------------------------------- /6.1-env-chisel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": { 5 | "image.png": { 6 | "image/png": "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" 7 | } 8 | }, 9 | "cell_type": "markdown", 10 | "metadata": {}, 11 | "source": [ 12 | "![image.png](attachment:image.png)" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "## Chisel Running Enviroments\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "import $ivy.`edu.berkeley.cs::chisel3:3.1.0` \n", 29 | "import $ivy.`edu.berkeley.cs::chisel-iotesters:1.2.0` " 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import chisel3._\n", 39 | "import chisel3.util._\n", 40 | "import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Utils Wrapper Function for Jupyter\n", 48 | "- getVerilog\n", 49 | "- getFirrtl\n", 50 | "- compileFIRRTL\n", 51 | "- stringifyAST" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "// Convenience function to invoke Chisel and grab emitted Verilog.\n", 61 | "def getVerilog(dut: => chisel3.core.UserModule): String = {\n", 62 | " import firrtl._\n", 63 | " return chisel3.Driver.execute(Array[String](), {() => dut}) match {\n", 64 | " case s:chisel3.ChiselExecutionSuccess => s.firrtlResultOption match {\n", 65 | " case Some(f:FirrtlExecutionSuccess) => f.emitted\n", 66 | " }\n", 67 | " }\n", 68 | "}\n", 69 | "\n", 70 | "// Convenience function to invoke Chisel and grab emitted FIRRTL.\n", 71 | "def getFirrtl(dut: => chisel3.core.UserModule): String = {\n", 72 | " return chisel3.Driver.emit({() => dut})\n", 73 | "}\n", 74 | "\n", 75 | "def compileFIRRTL(\n", 76 | " inputFirrtl: String,\n", 77 | " compiler: firrtl.Compiler,\n", 78 | " customTransforms: Seq[firrtl.Transform] = Seq.empty,\n", 79 | " infoMode: firrtl.Parser.InfoMode = firrtl.Parser.IgnoreInfo,\n", 80 | " annotations: firrtl.AnnotationSeq = firrtl.AnnotationSeq(Seq.empty)\n", 81 | "): String = {\n", 82 | " import firrtl.{Compiler, AnnotationSeq, CircuitState, ChirrtlForm, FIRRTLException}\n", 83 | " import firrtl.Parser._\n", 84 | " import scala.io.Source\n", 85 | " import scala.util.control.ControlThrowable\n", 86 | " import firrtl.passes._\n", 87 | " val outputBuffer = new java.io.CharArrayWriter\n", 88 | " try {\n", 89 | " //val parsedInput = firrtl.Parser.parse(Source.fromFile(input).getLines(), infoMode)\n", 90 | " val parsedInput = firrtl.Parser.parse(inputFirrtl.split(\"\\n\").toIterator, infoMode)\n", 91 | " compiler.compile(\n", 92 | " CircuitState(parsedInput, ChirrtlForm, annotations),\n", 93 | " outputBuffer,\n", 94 | " customTransforms)\n", 95 | " }\n", 96 | "\n", 97 | " catch {\n", 98 | " // Rethrow the exceptions which are expected or due to the runtime environment (out of memory, stack overflow)\n", 99 | " case p: ControlThrowable => throw p\n", 100 | " case p: PassException => throw p\n", 101 | " case p: FIRRTLException => throw p\n", 102 | " // Treat remaining exceptions as internal errors.\n", 103 | " case e: Exception => firrtl.Utils.throwInternalError(exception = Some(e))\n", 104 | " }\n", 105 | "\n", 106 | " val outputString = outputBuffer.toString\n", 107 | " outputString\n", 108 | "}\n", 109 | "\n", 110 | "def stringifyAST(firrtlAST: firrtl.ir.Circuit): String = {\n", 111 | " var ntabs = 0\n", 112 | " val buf = new StringBuilder\n", 113 | " val string = firrtlAST.toString\n", 114 | " string.zipWithIndex.foreach { case (c, idx) =>\n", 115 | " c match {\n", 116 | " case ' ' =>\n", 117 | " case '(' =>\n", 118 | " ntabs += 1\n", 119 | " buf ++= \"(\\n\" + \"| \" * ntabs\n", 120 | " case ')' =>\n", 121 | " ntabs -= 1\n", 122 | " buf ++= \"\\n\" + \"| \" * ntabs + \")\"\n", 123 | " case ','=> buf ++= \",\\n\" + \"| \" * ntabs\n", 124 | " case c if idx > 0 && string(idx-1)==')' =>\n", 125 | " buf ++= \"\\n\" + \"| \" * ntabs + c\n", 126 | " case c => buf += c\n", 127 | " }\n", 128 | " }\n", 129 | " buf.toString\n", 130 | "} " 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "# Your First Module" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "class PassThrough extends Module{\n", 147 | " val io = IO(new Bundle{\n", 148 | " val in = Input(UInt(8.W))\n", 149 | " val out = Output(UInt(8.W))\n", 150 | " })\n", 151 | " io.in := io.out\n", 152 | "} \n", 153 | "println(getVerilog(new Passthrough))" 154 | ] 155 | } 156 | ], 157 | "metadata": { 158 | "kernelspec": { 159 | "display_name": "Scala", 160 | "language": "scala", 161 | "name": "scala" 162 | }, 163 | "language_info": { 164 | "codemirror_mode": "text/x-scala", 165 | "file_extension": ".scala", 166 | "mimetype": "text/x-scala", 167 | "name": "scala", 168 | "nbconvert_exporter": "script", 169 | "version": "2.12.8" 170 | }, 171 | "toc": { 172 | "base_numbering": 1, 173 | "nav_menu": {}, 174 | "number_sections": true, 175 | "sideBar": true, 176 | "skip_h1_title": false, 177 | "title_cell": "Table of Contents", 178 | "title_sidebar": "Contents", 179 | "toc_cell": false, 180 | "toc_position": {}, 181 | "toc_section_display": true, 182 | "toc_window_display": false 183 | } 184 | }, 185 | "nbformat": 4, 186 | "nbformat_minor": 2 187 | } 188 | -------------------------------------------------------------------------------- /7.1-Scala-case-pattern.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 17 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "## Pattern matching\n", 25 | "\n", 26 | "- Normal case pattern \n", 27 | "```scala\n", 28 | "x match {\n", 29 | " case xx => ... \n", 30 | " case zz => ...\n", 31 | " case _ => ...\n", 32 | "}\n", 33 | "```\n", 34 | "- class Type self match \n", 35 | "```scala\n", 36 | "class C { self => } // Give this an alias of self, this.xxx equal self.xxx \n", 37 | "class C { this:X => } // Subclass of C must be mixed with X Trait \n", 38 | "class D extends C with X\n", 39 | "```\n", 40 | "- Seq-iterator Matching \n", 41 | "```scala\n", 42 | "List(\"a\",\"b\",\"c\",\"d\").map(x =>..)\n", 43 | "List(\"a\",\"b\",\"c\",\"d\").map(_)\n", 44 | "List(\"a\",\"b\",\"c\",\"d\").zipWithIndex.map((x,i)=>...) \n", 45 | "List(\"a\",\"b\",\"c\",\"d\").zipWithIndex.map(_ _) \n", 46 | "```\n", 47 | "- Assignment matching \n", 48 | "```scala\n", 49 | "val (a,b) = (1,2)\n", 50 | "val Students(Name,Age) = Students(\"jack\", 18)\n", 51 | "```" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "trait X\n", 61 | "class C { this:X => }\n", 62 | "class E extends C with X" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "val (a,b) = (1,2)\n", 72 | "case class Students(name:String, age:Int)\n", 73 | "val Students(name0,age0) = Students(\"jack\", 18)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "Ubiquitous pattern matching\n", 81 | "```scala\n", 82 | "object BusInterface {\n", 83 | " def apply(bus: Apb3, sizeMap: SizeMapping, selID: Int): BusIf = Apb3BusInterface(bus, sizeMap, selID)(moduleName)\n", 84 | " def apply(bus: Apb3, sizeMap: SizeMapping, selID: Int, readSync: Boolean): BusIf = Apb3BusInterface(bus, sizeMap, selID, readSync)(moduleName)\n", 85 | "\n", 86 | " def apply(bus: AhbLite3, sizeMap: SizeMapping): BusIf = AhbLite3BusInterface(bus, sizeMap)\n", 87 | " def apply(bus: AhbLite3, sizeMap: SizeMapping, readSync: Boolean): BusIf = AhbLite3BusInterface(bus, sizeMap, readSync)\n", 88 | "\n", 89 | " def apply(bus: Axi4, sizeMap: SizeMapping): BusIf = Axi4BusInterface(bus, sizeMap)\n", 90 | " def apply(bus: Axi4, sizeMap: SizeMapping, readSync: Boolean): BusIf = Axi4BusInterface(bus, sizeMap)\n", 91 | "\n", 92 | " def apply(bus: AxiLite4, sizeMap: SizeMapping): BusIf = AxiLite4BusInterface(bus, sizeMap)\n", 93 | " def apply(bus: AxiLite4, sizeMap: SizeMapping, readSync: Boolean): BusIf = AxiLite4BusInterface(bus, sizeMap)\n", 94 | "}\n", 95 | "```" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [] 104 | } 105 | ], 106 | "metadata": { 107 | "kernelspec": { 108 | "display_name": "Scala", 109 | "language": "scala", 110 | "name": "scala" 111 | }, 112 | "language_info": { 113 | "codemirror_mode": "text/x-scala", 114 | "file_extension": ".scala", 115 | "mimetype": "text/x-scala", 116 | "name": "scala", 117 | "nbconvert_exporter": "script", 118 | "version": "2.12.8" 119 | } 120 | }, 121 | "nbformat": 4, 122 | "nbformat_minor": 2 123 | } 124 | -------------------------------------------------------------------------------- /7.2-Scala-implicit.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote." 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Implicit\n", 33 | "### Example " 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "class Rational(val n: Int, val d:Int) {\n", 43 | " require(d != 0)\n", 44 | " override def toString = n + \"/\" + d\n", 45 | " def +(x: Rational): Rational = new Rational(this.n*x.d + x.n*this.d, this.d * x.d)\n", 46 | " def +(x: Int): Rational = new Rational(this.n + x*this.d, this.d)\n", 47 | "}\n", 48 | "val a = new Rational(2,3)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "val b = new Rational(1,4)\n", 58 | "val r0 = a + b\n", 59 | "val r1 = a + 2" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "val r2 = 2 + a" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "This cause an error, you can find `Int 2` have not method `+(x: Rtional)`" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "we have 2 way to implement that \n", 83 | "\n", 84 | "**way1 translate Int to Rational** \n", 85 | "There's nothing to discuss about explicit transformation, This is what we do in general languages\n", 86 | "```\n", 87 | "val r2 = new Ration(2,1) + a\n", 88 | "```\n", 89 | "If there is a large number of such operations, it is obviously very cumbersome. so an implicit transform is very elegant" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "implicit def transInt2Rational(x: Int) = new Rational(x,1)\n", 99 | "val r2 = 2 + a" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "**way2 add a method of `+(x: Rational)` to Int** \n", 107 | "\n", 108 | "We can't modify Scala's source code to add methods to Int. \n", 109 | "\n", 110 | "But Scala's fascinating implicit transformation allows us to add methods to a type without modifying the source code, as if the type itself had its own methods" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "implicit class expandInt(t: Int){\n", 120 | " def +(x: Rational): Rational = new Rational(t,1) + x\n", 121 | "}\n", 122 | "val r2 = 2 + a" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## 1 Implicit Transform\n", 130 | "**transform only when needed, else do nothing**\n" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "case class Euro(value: Double)\n", 140 | "case class Dollar(value: Double)\n", 141 | "\n", 142 | "object Dollar{\n", 143 | " implicit def dollarToEuro(x: Dollar): Euro = Euro(x.value*0.75)\n", 144 | "}\n", 145 | "\n", 146 | "val a = Dollar(12)\n", 147 | "val b: Euro = Dollar(10)" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "val a = 3.001 * 2" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "val b = 2 * 3.0001 \n", 166 | "\n", 167 | "object Int {\n", 168 | " def *(x :Int): Int = this * x\n", 169 | " def *(x :Double): Double = Double(this) * x\n", 170 | "}" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "val x: Double = 2" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "val x:Int = 3.000" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "val x: Int = 3.001.toInt" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "## 2 implicit Extend\n", 205 | "**Silently enhance the Class without changing anything of the original Class **\n" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "## 3 implicit Parameter\n", 213 | "**Implicitly define a parameter and pass it in implicitly, which is transparent to users**\n" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "object Test{\n", 223 | " def show(implicit h: Euro) = {\n", 224 | " println(h.value)\n", 225 | " }\n", 226 | "}\n", 227 | "implicit val h = Euro(12)\n", 228 | "Test.show" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "## 4 implicit Generator(Cooperation of implicit transform and parameter)\n", 236 | "\n", 237 | "**purpose**:generate one if not exist else do nothing \n", 238 | "**difference**:\"implicit transform\" with parameter,*implicti generator*: without parameter\n", 239 | "\n", 240 | "*if there have no explicit parameter , Implicit generator are triggered, and there will get on generator object*\n", 241 | "```scala\n", 242 | "def hello(implicit h: ClassName) = {}\n", 243 | "...\n", 244 | "Test.hello //when the have no parameter , implicit generator one\n", 245 | "```\n" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "case class ClassName(name: String)\n", 255 | "object ClassName {\n", 256 | " //generator on when needed \n", 257 | " implicit def genWhenNeed: ClassName = ClassName(\"Example\")\n", 258 | "}\n", 259 | "object Test{\n", 260 | " def hello(implicit h: ClassName) = {\n", 261 | " println(h.name)\n", 262 | " }\n", 263 | "}\n", 264 | "Test.hello(ClassName(\"jack\"))\n", 265 | "Test.hello" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "In addition to the induction triggered implicit generate\n", 273 | "\n", 274 | "**You can also manually get an implicit object by`implicitly[]`**,attention it's`[]`, not`()`\n", 275 | "\n", 276 | "```scala\n", 277 | "val b = implicitly[ClassName]\n", 278 | "```" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": null, 284 | "metadata": {}, 285 | "outputs": [], 286 | "source": [ 287 | "val b = implicitly[ClassName]" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## 5 implicit generator and Macro\n", 295 | "\n", 296 | "This provides a way to deal with compilers\n", 297 | "\n", 298 | "- Way 1\n", 299 | "```scala \n", 300 | "def getLineNumber(implicit line: LineNumber) ={\n", 301 | " println(\"add at ${line}\"\n", 302 | " line\n", 303 | "}\n", 304 | "val s = getLineNumber\n", 305 | "```\n", 306 | "\n", 307 | "- Way2\n", 308 | "```scala\n", 309 | "val s = implicitly[LineNumber]\n", 310 | "```\n", 311 | "\n", 312 | "\n", 313 | "(*Note: Scala macro have some problems in jupyter env. Please compile and run the instance in SBT environment*)\n" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "//Gets the current number of lines of code\n", 323 | "import language.experimental.macros\n", 324 | "import reflect.macros.blackbox.Context\n", 325 | "\n", 326 | "case class LineNumber(no: Int)\n", 327 | "\n", 328 | "object LineNumber {\n", 329 | " implicit def genWhenNeed: LineNumber = macro Macros.lineNumberImpl\n", 330 | "}\n", 331 | "\n", 332 | "object Macros{\n", 333 | " def lineNumberImpl(c: Context): c.Expr[LineNumber] = {\n", 334 | " import c.universe._\n", 335 | " val lineNumber = c.enclosingPosition.line\n", 336 | " c.Expr[LineNumber](q\"\"\"${c.prefix}($lineNumber)\"\"\")\n", 337 | " }\n", 338 | "}" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "//Get variable name\n", 348 | "import language.experimental.macros\n", 349 | "import reflect.macros.blackbox.Context\n", 350 | "\n", 351 | "case class SymbolName(name: String)\n", 352 | "case class ClassName(name: String)\n", 353 | "\n", 354 | "object SymbolName{\n", 355 | " implicit def genWhenNeed: SymbolName = macro Macros.symbolNameImpl\n", 356 | "}\n", 357 | " \n", 358 | "\n", 359 | "object Macros{\n", 360 | " def symbolNameImpl(c: Context): c.Expr[SymbolName] = {\n", 361 | " import c.universe._\n", 362 | " val symbolName = c.internal.enclosingOwner.name.decodedName.toString.trim\n", 363 | " c.Expr[SymbolName](q\"\"\"${c.prefix}($symbolName)\"\"\")\n", 364 | " }\n", 365 | "}" 366 | ] 367 | }, 368 | { 369 | "cell_type": "markdown", 370 | "metadata": {}, 371 | "source": [ 372 | "## Creat a Complex from Zero\n", 373 | "```scala\n", 374 | "val a = 2 + 3.2*j \n", 375 | "val b = 3.001 -7*j\n", 376 | "val c = a * b\n", 377 | "println(c)\n", 378 | "28.402 - 4.397j\n", 379 | "```" 380 | ] 381 | }, 382 | { 383 | "cell_type": "code", 384 | "execution_count": null, 385 | "metadata": {}, 386 | "outputs": [], 387 | "source": [ 388 | "import scala.math._\n", 389 | "case class Complex(re: Double, im: Double) {\n", 390 | " def +(x: Complex): Complex = Complex(re + x.re, im + x.im)\n", 391 | " def -(x: Complex): Complex = Complex(re - x.re, im - x.im)\n", 392 | " def *(x: Double): Complex = Complex(re * x, im * x)\n", 393 | " def *(x: Complex): Complex = Complex(re * x.re - im * x.im, re * x.im + im * x.re)\n", 394 | " def /(x: Double): Complex = Complex(re / x, im / x)\n", 395 | " \n", 396 | " override def toString(): String = {\n", 397 | " val a = \"%1.3f\".format(re)\n", 398 | " val b = \"%1.3f\".format(abs(im))\n", 399 | " (a,b) match {\n", 400 | " case (_, \"0.000\") => a\n", 401 | " case (\"0.000\", _) => b + \"j\"\n", 402 | " case (_, _) if im > 0 => a + \" + \" + b + \"j\"\n", 403 | " case (_, _) => a + \" - \" + b + \"j\"\n", 404 | " }\n", 405 | " }\n", 406 | "} " 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": null, 412 | "metadata": {}, 413 | "outputs": [], 414 | "source": [ 415 | "trait jbase\n", 416 | "object j extends jbase\n", 417 | "case class image(value: Double)\n", 418 | "implicit class compleInt(x: Int) {\n", 419 | " def *(y: jbase) = image(x toDouble)\n", 420 | "}\n", 421 | "implicit class compleDouble(x: Double) {\n", 422 | " def *(y: jbase) = image(x)\n", 423 | "}\n", 424 | "implicit class DoubleExpand(x: Double) {\n", 425 | " def +(that: image) = Complex(x,that.value)\n", 426 | " def -(that: image) = Complex(x,-that.value)\n", 427 | "}\n", 428 | "implicit class intExpand(x: Int) {\n", 429 | " def +(that: image) = Complex(x toDouble,that.value)\n", 430 | " def -(that: image) = Complex(x toDouble,-that.value)\n", 431 | "}" 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": null, 437 | "metadata": {}, 438 | "outputs": [], 439 | "source": [ 440 | "val a = 2 + 3.2*j \n", 441 | "val b = 3.001 -7*j\n", 442 | "val c = a * b\n", 443 | "println(c)" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": null, 449 | "metadata": {}, 450 | "outputs": [], 451 | "source": [ 452 | "val N = 64\n", 453 | "(0 to N/4).map(2*Pi*_/N).map(t => cos(t)+sin(t)*j )" 454 | ] 455 | } 456 | ], 457 | "metadata": { 458 | "kernelspec": { 459 | "display_name": "Scala", 460 | "language": "scala", 461 | "name": "scala" 462 | }, 463 | "language_info": { 464 | "codemirror_mode": "text/x-scala", 465 | "file_extension": ".scala", 466 | "mimetype": "text/x-scala", 467 | "name": "scala", 468 | "nbconvert_exporter": "script", 469 | "version": "2.12.8" 470 | } 471 | }, 472 | "nbformat": 4, 473 | "nbformat_minor": 2 474 | } 475 | -------------------------------------------------------------------------------- /7.4-Scala-Macro.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | " Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## Macro\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import language.experimental.macros\n", 42 | "\n", 43 | "// Normal imports\n", 44 | "import reflect.macros.blackbox.Context\n", 45 | "\n", 46 | "object Step1Complete {\n", 47 | " def hello(): Unit = macro hello_impl\n", 48 | "\n", 49 | " def hello_impl(c: Context)(): c.Expr[Unit] = {\n", 50 | " import c.universe._\n", 51 | " println(\"Compiling!\")\n", 52 | " reify { println(\"Hello World!\") }\n", 53 | " }\n", 54 | " \n", 55 | "} " 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## quasiquotes \n", 63 | "https://docs.scala-lang.org/overviews/quasiquotes/setup.html \n", 64 | "Compared with LISP's macro" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe\n", 74 | "import universe._\n", 75 | "val tree = q\"i am { a quasiquote }\"\n", 76 | "val C = q\"class C\"\n", 77 | "//println(showCode(C))\n", 78 | "//println(showCode(tree))\n", 79 | "//println(showRaw(tree))" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": null, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "val a = q\"true\"\n", 89 | "val b = true" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "val q\"i am $what\" = q\"i am { a quasiquote }\"" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "val ab = List(q\"a\", q\"b\")" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "import scala.reflect.runtime.currentMirror\n", 117 | "import scala.tools.reflect.ToolBox\n", 118 | "val toolbox = currentMirror.mkToolBox()\n", 119 | "val fab = q\"f(..$ab)\"\n", 120 | "//val C = q\"class C\"\n", 121 | "val tree = q\"i am { a quasiquote }\"\n", 122 | "println(tree match { case q\"i am { a quasiquote }\" => \"it worked!\" })" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "println(q\"foo + bar\" equalsStructure q\"foo.+(bar)\")" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "val x = q\"\"\"\n", 141 | " val x: List[Int] = List(1, 2) match {\n", 142 | " case List(a, b) => List(a + b)\n", 143 | " }\n", 144 | " \"\"\"" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "val q\"f(..$args)\" = q\"f(a, b)\"" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "val two = 1 + 1\n", 163 | "val four = q\"$two + $two\"\n", 164 | "val ints = List(1, 2, 3)\n", 165 | "val f123 = q\"f(..$ints)\"" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "val q\"${left: Int} + ${right: Int}\" = q\"2 + 2\"" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "val q\"..$stats\" = q\"(2,3)\"" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "val code = q\"\"\"println(\"compiled and run at runtime!\")\"\"\"\n", 193 | "val compiledCode = toolbox.compile(code)\n", 194 | "val result = compiledCode()" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "val x = q\"package mypackage { class MyClass }\"\n", 204 | "showCode(x)" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | " \n", 214 | " def generateCode() =\n", 215 | " q\"package mypackage { class MyClass }\"\n", 216 | " def saveToFile(path: String, code: Tree) = {\n", 217 | " val writer = new java.io.PrintWriter(path)\n", 218 | " try writer.write(showCode(code))\n", 219 | " finally writer.close()\n", 220 | " }\n", 221 | " saveToFile(\"myfile.scala\", generateCode())\n", 222 | " " 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": { 229 | "scrolled": true 230 | }, 231 | "outputs": [], 232 | "source": [ 233 | "val q\"$expr.$tname\" = q\"a.eat\"\n", 234 | "val q\"${x: scala.Symbol}\" = q\"'a\"\n" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "val q\"$mods def $tname[..$tparams](...$paramss): $tpt = $expr\" = q\"\"\"\n", 244 | "private def interrupt(namePre:String, triggers:Int*)(name:String): Unit = \n", 245 | "println(\"hello scala macro\")\n", 246 | "\"\"\"" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": null, 252 | "metadata": {}, 253 | "outputs": [], 254 | "source": [ 255 | "val q\"..$stats0\" = q\"a; b; c\"\n", 256 | "val q\"..$stats1\" = q\"(a, b, c)\"\n", 257 | "val q\"..$stats2\" = q\"\"\"{a\n", 258 | "b\n", 259 | "c}\"\"\"\n", 260 | "val q\"..$stats3\" = q\"\"\"{\n", 261 | "val a: Int = 1\n", 262 | "val b: Int = 2\n", 263 | "val c = 3}\"\"\"" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | " val extractor = pq\"Foo(1, 2, 3)\"\n", 273 | " val extractor2 = q\"Foo(1, 2, 3)\"\n", 274 | "val pq\"$id(..$pats)\" = extractor\n", 275 | "val pq\"$id2(..$pats2)\" = extractor2" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "val isT = pq\"_: T\"" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "val f3 = q\"private implicit def f\"" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "import language.experimental.macros\n", 303 | "\n", 304 | "// Normal imports\n", 305 | "import reflect.macros.blackbox.Context" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "abstract class Animal {\n", 315 | " def name: String\n", 316 | "}\n", 317 | "case class Cat(name: String) extends Animal\n", 318 | "case class Dog(name: String) extends Animal" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": null, 324 | "metadata": {}, 325 | "outputs": [], 326 | "source": [ 327 | "object CovarianceTest extends App {\n", 328 | " def printAnimalNames(animals: List[Animal]): Unit = {\n", 329 | " animals.foreach { animal =>\n", 330 | " println(animal.name)\n", 331 | " }\n", 332 | " }\n", 333 | "\n", 334 | " val cats: List[Cat] = List(Cat(\"Whiskers\"), Cat(\"Tom\"))\n", 335 | " val dogs: List[Dog] = List(Dog(\"Fido\"), Dog(\"Rex\"))\n", 336 | "\n", 337 | " printAnimalNames(cats)\n", 338 | " // Whiskers\n", 339 | " // Tom\n", 340 | "\n", 341 | " printAnimalNames(dogs)\n", 342 | " // Fido\n", 343 | " // Rex\n", 344 | "}" 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": null, 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [ 353 | "//CovarianceTest.printAnimalNames(CovarianceTest.cats)\n", 354 | " val cats: List[Cat] = List(Cat(\"Whiskers\"), Cat(\"Tom\"))\n", 355 | " val dogs: List[Dog] = List(Dog(\"Fido\"), Dog(\"Rex\"))\n", 356 | "CovarianceTest.printAnimalNames(cats)\n", 357 | " CovarianceTest.printAnimalNames(dogs)" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": null, 363 | "metadata": {}, 364 | "outputs": [], 365 | "source": [ 366 | "println(HTMLFactory)" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": null, 372 | "metadata": {}, 373 | "outputs": [], 374 | "source": [ 375 | "val htmlbody = \"html5 ${body} end\"\n", 376 | "val body = \"-body-\"\n", 377 | "StringContext(\"html5 \",\"end\").s(body)" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": {}, 384 | "outputs": [], 385 | "source": [ 386 | "val body = \"div xxx div \"\n", 387 | "val b = s\"html5 ${body} end\" \n", 388 | "val c = new StringContext(htmlbody).s(htmlbody)\n", 389 | "val d = StringContext(\"html5 ${body} end\")\n", 390 | " \n", 391 | "//val bf = \"html5 %s end\".format(body)" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": null, 397 | "metadata": {}, 398 | "outputs": [], 399 | "source": [ 400 | "class T1 extends Component{\n", 401 | " val f = UInt\"11000011\"\n", 402 | "}\n", 403 | "showRtl(new T1)" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": null, 409 | "metadata": {}, 410 | "outputs": [], 411 | "source": [ 412 | "import scala.reflect.macros.whitebox.Context\n", 413 | "import scala.language.experimental.macros\n", 414 | "import scala.annotation.StaticAnnotation\n", 415 | "object helloMacro {\n", 416 | " def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {\n", 417 | " import c.universe._\n", 418 | " val result = {\n", 419 | " annottees.map(_.tree).toList match {\n", 420 | " case ModuleDef(mods, name, Template(parents, self, body)) :: Nil =>\n", 421 | " val helloMethod = DefDef(NoMods, TermName(\"hello\"), List(), List(List()), TypeTree(), Literal(Constant(\"hello\")))\n", 422 | " ModuleDef(mods, name, Template(parents, self, body :+ helloMethod))\n", 423 | " }\n", 424 | " }\n", 425 | " c.Expr[Any](result)\n", 426 | " }\n", 427 | "}" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": null, 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [ 436 | "import scala.reflect.macros.blackbox\n", 437 | "object dataMacro {\n", 438 | " def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {\n", 439 | " import c.universe._\n", 440 | "\n", 441 | " val result = {\n", 442 | " annottees.map(_.tree).toList match {\n", 443 | " case q\"\"\"\n", 444 | " class $name {\n", 445 | " ..$vars\n", 446 | " }\n", 447 | " \"\"\" :: Nil =>\n", 448 | "\n", 449 | " // Generate the Getter and Setter from VarDefs\n", 450 | " val beanMethods = vars.collect {\n", 451 | " case q\"$mods var $name: $tpt = $expr\" =>\n", 452 | " val getName = TermName(\"get\" + name.encodedName.toString.capitalize)\n", 453 | " val setName = TermName(\"set\" + name.encodedName.toString.capitalize)\n", 454 | " println(getName)\n", 455 | " val ident = Ident(name)\n", 456 | " List (\n", 457 | " q\"def $getName: $tpt = $ident\",\n", 458 | " q\"def $setName(paramX: $tpt): Unit = { $ident = paramX }\"\n", 459 | " )\n", 460 | " }.flatten\n", 461 | "\n", 462 | " // Insert the generated Getter and Setter\n", 463 | " q\"\"\"\n", 464 | " class $name {\n", 465 | " ..$vars\n", 466 | " ..$beanMethods\n", 467 | " }\n", 468 | " \"\"\"\n", 469 | " case _ =>\n", 470 | " throw new Exception(\"Macro Error\")\n", 471 | " }\n", 472 | " }\n", 473 | " c.Expr[Any](result)\n", 474 | " }\n", 475 | "}\n" 476 | ] 477 | }, 478 | { 479 | "cell_type": "code", 480 | "execution_count": null, 481 | "metadata": {}, 482 | "outputs": [], 483 | "source": [ 484 | "class data extends StaticAnnotation {\n", 485 | " def macroTransform(annottees: Any*): Any = macro dataMacro.impl\n", 486 | "}" 487 | ] 488 | } 489 | ], 490 | "metadata": { 491 | "kernelspec": { 492 | "display_name": "Scala", 493 | "language": "scala", 494 | "name": "scala" 495 | }, 496 | "language_info": { 497 | "codemirror_mode": "text/x-scala", 498 | "file_extension": ".scala", 499 | "mimetype": "text/x-scala", 500 | "name": "scala", 501 | "nbconvert_exporter": "script", 502 | "version": "2.12.8" 503 | } 504 | }, 505 | "nbformat": 4, 506 | "nbformat_minor": 2 507 | } 508 | -------------------------------------------------------------------------------- /7.5-Scala-DataType.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "# DataType\n", 33 | "\n", 34 | "- Boolean\n", 35 | "- Byte\n", 36 | "- Short\n", 37 | "- Int\n", 38 | "- Long //64bit\n", 39 | "- Float //32bit\n", 40 | "- Double//64bit \n", 41 | "- BigInt \n", 42 | "- BigDecimal " 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "# Epsilon in Double\n", 50 | "\n", 51 | "**precious depends on exponent** \n", 52 | "\n", 53 | "- FloatError : `pow(2, exponent) * 1/pow(2,23))`\n", 54 | "- DoubleError : `pow(2, exponent) * 1/pow(2,52))`\n", 55 | "\n", 56 | "when exponent = 0, we got Epsilon \n", 57 | "- FloatEps : `1/pow(2,23) = pow(2,-23) = 1.1920928955078125E-7`\n", 58 | "- DoubleEps : `1/pow(2,52) = pow(2,-52) = 2.220446049250313E-16`" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "scala.math.pow(2,-23)\n", 68 | "scala.math.pow(2,-52)" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "\n", 76 | "\n", 77 | "## Float\n", 78 | "**1 sign, 8 bits exponent, 23 bits mantissa** \n", 79 | "```scala\n", 80 | "sign * 2^(2^exponent) * mantissa(2^23-1) \n", 81 | "absMaxValue = ±1 * (mantissa52bit << 127 ) = ±pow(2, 127) * (pow(2,23)-1)/pow(2,23))\n", 82 | "absminValue = 1 * (mantissa52bit >> 127 ) = pow(2,-128) * (pow(2,23)-1)/pow(2,23))\n", 83 | "```\n", 84 | " \n", 85 | "## Double\n", 86 | "**1 sign, 11 bits exponent, 52 bits mantissa** \n", 87 | "```scala\n", 88 | "sign * 2^(2^exponent) * mantissa(2^52-1) \n", 89 | "absMaxValue = ±1 * (mantissa52bit << 1024 ) = ±pow(2, 1023) * (pow(2,52)-1)/pow(2,52))\n", 90 | "absminValue = 1 * (mantissa52bit >> 1024 ) = pow(2,-1024) * (pow(2,52)-1)/pow(2,52)) \n", 91 | "```\n" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "scala.math.pow(2,-50)\n", 101 | "scala.math.pow(2,-51)\n", 102 | "scala.math.pow(2,-52)\n", 103 | "scala.math.pow(2,-62)\n", 104 | "scala.math.pow(2,-100) \n", 105 | "scala.math.pow(2,-100) + scala.math.pow(2,-100)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [] 114 | } 115 | ], 116 | "metadata": { 117 | "kernelspec": { 118 | "display_name": "Scala", 119 | "language": "scala", 120 | "name": "scala" 121 | }, 122 | "language_info": { 123 | "codemirror_mode": "text/x-scala", 124 | "file_extension": ".scala", 125 | "mimetype": "text/x-scala", 126 | "name": "scala", 127 | "nbconvert_exporter": "script", 128 | "version": "2.12.8" 129 | }, 130 | "toc": { 131 | "base_numbering": 1, 132 | "nav_menu": {}, 133 | "number_sections": true, 134 | "sideBar": true, 135 | "skip_h1_title": false, 136 | "title_cell": "Table of Contents", 137 | "title_sidebar": "Contents", 138 | "toc_cell": false, 139 | "toc_position": {}, 140 | "toc_section_display": true, 141 | "toc_window_display": false 142 | } 143 | }, 144 | "nbformat": 4, 145 | "nbformat_minor": 2 146 | } 147 | -------------------------------------------------------------------------------- /7.6-Scala-FAQ.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "\"SpinalHDL" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Before running Spinal HDL code, be sure to load SpinalHDL Libraries \n", 15 | "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.) " 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n", 25 | "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "## 1. val def in Trait\n", 33 | "**Attation** val cause Error " 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "trait PRNBase {\n", 43 | " val size: Int \n", 44 | "\n", 45 | " val Mask = (1 << size) - 1\n", 46 | " val Msb = (1 << (size - 1))\n", 47 | "}\n", 48 | "\n", 49 | "object GPS extends PRNBase{\n", 50 | " val size = 1023\n", 51 | "}\n", 52 | "\n", 53 | "object BD extends PRNBase{\n", 54 | " val size = 2046\n", 55 | "}\n", 56 | "BD.Mask toHexString // return 0" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "trait PRNBase {\n", 66 | " val size: Int \n", 67 | "\n", 68 | " def Mask = (1 << size) - 1\n", 69 | " def Msb = (1 << (size - 1))\n", 70 | "}\n", 71 | "object BD extends PRNBase{\n", 72 | " val size = 11\n", 73 | "}\n", 74 | "BD.Mask toHexString" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## 2. Scala double definition \n", 82 | "```scala \n", 83 | "class TestDoubleDef{\n", 84 | " def foo(p:List[String]) = {}\n", 85 | " def foo(p:List[Int]) = {}\n", 86 | "}\n", 87 | "```\n", 88 | "**raise Error:**\n", 89 | "```sh\n", 90 | "[error] double definition:\n", 91 | "[error] method foo:(List[String])Unit and\n", 92 | "[error] method foo:(List[Int])Unit at line 120\n", 93 | "[error] have same type after erasure: (List)Unit \n", 94 | "```\n", 95 | "Solution: https://stackoverflow.com/questions/3307427/scala-double-definition-2-methods-have-the-same-type-erasure/3544060#3544060" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "object MyTransform{\n", 105 | " def apply(x: Int ): Double = x + 0.00 \n", 106 | " def apply(x: List[Int] ): List[Double] = x.map(_+0.00)\n", 107 | " def apply(x: List[Double] ): List[Double] = x.map(_+0.00)\n", 108 | "}" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "**Use implict transform**" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "case class IntList(list: List[Int])\n", 125 | "case class LongList(list: List[Double])\n", 126 | "case class DoubleList(list: List[Double])\n", 127 | "\n", 128 | "implicit def Il(list: List[Int]) = IntList(list)\n", 129 | "implicit def Ll(list: List[Double]) = LongList(list)\n", 130 | "implicit def Dl(list: List[Double]) = DoubleList(list)\n", 131 | "\n", 132 | "object FixTo{\n", 133 | " def apply(x: Int ): Double = x + 0.00 \n", 134 | " def apply(x: IntList ): List[Double] = x.list.map(_+0.00)\n", 135 | " def apply(x: LongList ): List[Double] = x.list.map(_+0.00)\n", 136 | " def apply(x: DoubleList ): List[Double] = x.list.map(_+0.00)\n", 137 | "}" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "val a = FixTo(3)\n", 147 | "val b = FixTo(DoubleList(List(1,2,3,4,5)))\n", 148 | "val c = FixTo(List(1,2,3,4,5))" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | " " 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "## 3. Timethis \n", 165 | "```scala \n", 166 | " val beg = System.currentTimeMillis()\n", 167 | " ... block \n", 168 | " val gap = System.currentTimeMillis() - beg\n", 169 | " println(s\"time consume $gap ms\")\n", 170 | "```" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | " val beg = System.currentTimeMillis()" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "## 4. Peformance \n", 187 | "```scala \n", 188 | " val data = List.fill(100000)(rand.nextInt)\n", 189 | " val sum = data.foldLeft(0)(_+_)\n", 190 | " val content = data.map(_.toString).foldLeft(\"\")(_+_) // Attation!! bad-performance\n", 191 | " val content = data.mkString(\"\\n\") // this is recommended\n", 192 | " import java.io.PrintWriter\n", 193 | " new PrintWriter(\"./test.txt\"){write(content);close} \n", 194 | "```\n", 195 | "\n", 196 | "So it's better not to use foldLeft/foldRight for string splicing" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "## 5. Sequence slicing\n", 204 | "- Way1: `map(i => source(i * step))` **×**\n", 205 | "- Way2: `source.sliding(step, step).map(_.head).toList` **√**\n", 206 | "- Way3: `map(i => source(i * step))` **×**" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "#### Way1 `15s`" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "val rand = new scala.util.Random(0)\n", 223 | "val source = List.fill(16384*16)(rand.nextDouble())\n", 224 | "val step = 8\n", 225 | "SpinalProgress(\"start\")\n", 226 | "//val x = (0 until source.size/step).map(i => source(i*step))\n", 227 | "SpinalProgress(\"done\")" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "#### Way2 `0.03S` recomonded\n", 235 | "equal\n", 236 | "```scala \n", 237 | "val x = source.grouped(step).map(_.head).toList\n", 238 | "```" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "SpinalProgress(\"start\")\n", 248 | "val x = source.sliding(step, step).map(_.head).toList\n", 249 | "SpinalProgress(\"done\")" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "#### Way3 `45s`" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "SpinalProgress(\"start\")\n", 266 | "val x = for(i <- 0 until source.size/step) yield source(i*downTimes)\n", 267 | "SpinalProgress(\"done\")" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [] 276 | } 277 | ], 278 | "metadata": { 279 | "kernelspec": { 280 | "display_name": "Scala", 281 | "language": "scala", 282 | "name": "scala" 283 | }, 284 | "language_info": { 285 | "codemirror_mode": "text/x-scala", 286 | "file_extension": ".scala", 287 | "mimetype": "text/x-scala", 288 | "name": "scala", 289 | "nbconvert_exporter": "script", 290 | "version": "2.12.8" 291 | }, 292 | "toc": { 293 | "base_numbering": 1, 294 | "nav_menu": {}, 295 | "number_sections": true, 296 | "sideBar": true, 297 | "skip_h1_title": false, 298 | "title_cell": "Table of Contents", 299 | "title_sidebar": "Contents", 300 | "toc_cell": false, 301 | "toc_position": {}, 302 | "toc_section_display": true, 303 | "toc_window_display": false 304 | } 305 | }, 306 | "nbformat": 4, 307 | "nbformat_minor": 2 308 | } 309 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spinal-bootcamp [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jijingg/Spinal-bootcamp/binder) 2 | 3 | This is SpinalHDL Tutorial, More important, it's a spinalHDL run-time environment. 4 | 5 |
6 | 7 | Most of case come from [SpinalDoc](https://spinalhdl.github.io/SpinalDoc-RTD/index.html), 8 | In addition, we also introduced some advanced usage of Scala, which are very helpful to understand how spinalHDL works. 9 | 10 | **Why we do such a thing:** 11 | - Sometimes you just want to try a usage of a Spinal or Scala, but you don't want to create a new project. 12 | - Practice is important for learning a new skill. So we did a lot of examples for you to try run. 13 | 14 | ## Usage 15 | 16 | There are two ways, you can run online or locally 17 | 18 | ### Online 19 | 20 | click [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jijingg/Spinal-bootcamp/binder) and start 21 | 22 | ### Local 23 | 24 | ```shell 25 | $: git clone https://github.com/jijingg/Spinal-bootcamp 26 | $: cd Spinal-bootcamp 27 | $: jupyter notebook & 28 | ``` 29 | 30 | ## Setup Jupyter-notebook enviroment 31 | 32 | if you want run locally, install follows first 33 | - [jupyter-notebook](https://jupyter.org/install)(Strongly recommend installing Python and Jupyter using the [Anaconda](https://www.anaconda.com/distribution/)) 34 | - scala(Scala2.12 is recommended) 35 | - [almond](https://almond.sh/)(scala kernel for jupyter) 36 | 37 | ### Setup on Windows10 and higher 38 | 39 | [WSL](https://docs.microsoft.com/en-us/windows/wsl/) and [VS Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) is recommended. 40 | 41 | 1. Clone this repository and open a **WSL terminal** here. 42 | 2. Confirm your java and jupyter installation like this 43 | ``` 44 | ~# java -version 45 | openjdk version "1.8.0_312" 46 | OpenJDK Runtime Environment (build 1.8.0_312-8u312-b07-0ubuntu1~20.04-b07) 47 | OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode) 48 | ~# echo $JAVA_HOME 49 | /usr/lib/jvm/java-8-openjdk-amd64 50 | ~# pip list 51 | ``` 52 | 3. Install [almond](https://almond.sh/) for WSL (the same as Linux) 53 | ``` 54 | ~# cd source 55 | ~# chmod +x coursier 56 | ~# ./coursier launch --fork almond -- --install 57 | ``` 58 | 4. Open the folder with [VS Code](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) or jupyter 59 | 60 | `~# cd ..` 61 | 62 | | VS Code | jupyter lab | jupyter notebook | 63 | | :------: | :-----------: | :--------------: | 64 | | `code .` | `jupyter-lab` | `jupyter notebook --allow-root &` | 65 | 66 | - If you don't use VS Code, you would change the default browser of jupyter in WSL. 67 | 68 | ## Before the start 69 | 70 | We assume you already have the basics knowledge of Scala,So there is no introduction to scala syntax here. 71 | If you need to learn Scala, I would strongly recommend the book of **["Programming in Scala"](https://www.oreilly.com/library/view/programming-in-scala/9780981531687/)** by Martin Odersky who is also the author of Scala. And just 72 | try them as many as you can on jupyter notebook env, This may be more efficient to help you master Scala than just reading a book without practice. 73 | 74 | ## FAQ 75 | 76 | 1. Make sure the right version have been installed. 77 | 78 | - Java Version jdk1.8 (java11 not supported yet) 79 | - Scala Version 2.12.x (2.13.x not supported yet) 80 | - Almond Version 0.5.0 is recommended 81 | 82 | install Almond on MacOs 83 | ```shell 84 | brew install coursier/formulas/coursier 85 | cs launch --fork almond:0.5.0 --scala 2.11.12 -- --install --force 86 | ``` 87 | -------------------------------------------------------------------------------- /Toc.md: -------------------------------------------------------------------------------- 1 | 1. Spinal Basic 2 | 1.1 quick-start 3 | 1.2 design-rules-check 4 | 1.3 Data-type 5 | 1.4 basic 6 | 1.4.1 Combinationa 7 | 1.4.2 Sequential 8 | 1.5 Stucturing 9 | 1.5.2 Width-propogate 10 | 1.5.3 Component-Function-area 11 | 1.5.3 ClockDomain 12 | 1.6 State-machine 13 | 1.7 Mem/Rom 14 | 1.8 IO 15 | 16 | 2. Example 17 | 1.1 18 | 19 | 2. Spinal Lib 20 | 2.1 Utils 21 | 2.2 FixPoint 22 | 2.3 Regif 23 | 2.4 pipeLine 24 | 25 | 3. Bus 26 | 3.1 concept 27 | 3.1 handshake 28 | 3.2 arbiter 29 | 3.3 outstanding 30 | 3.2 flow/stream 31 | 3.3 AMBA 32 | 3.3.1 APB 33 | 3.3.2 AHB 34 | 3.3.1 AXI 35 | 3.4 Tilelink 36 | 3.5 Peri 37 | 3.5.1 UART 38 | 3.5.2 SPI 39 | 3.5.3 I2C 40 | 3.5.4 USB 41 | 3.6 Other 42 | 3.6.1 ICB 43 | 3.6.2 WishBone 44 | 45 | 4. Soc Design 46 | 4.1 Architecture 47 | 4.2 48 | 4.2 pinseSec 49 | 4.3 VexRsicv 50 | 4.4 NaxRiscv 51 | 52 | 6. Simulation 53 | 6.1 enviroment Setup 54 | 6.1.1 Windows 55 | 6.1.2 MacOs 56 | 6.1.3 Linux 57 | 6.2 Boot Simulation 58 | 6.2.1 verilator 59 | 6.2.2 Vcs+fsdb 60 | 6.2.3 Modsim 61 | 6.2.4 GHDL/Iverilog 62 | 6.2.5 Vivado 63 | 6.2 boot a simulation 64 | 6.3 Simulation API 65 | 6.3.1 set/get/sleep 66 | 6.3.2 fork/join 67 | 6.5 SVM(SpinalVerificationMethodology) 68 | 6.5.1 Agent 69 | 6.5.1.1 Sequencer 70 | 6.5.1.2 Driver 71 | 6.5.1.3 Moniter 72 | 6.5.2 ScoreBoard 73 | 74 | 7. Landing 75 | 7.1 CPU 76 | 7.2 ANN(artificial neural network) 77 | 7.3 ISP 78 | 7.4 NetWork 79 | 7.4.1 SmartNIC 80 | 7.4.2 DPU 81 | 7.4.3 RDMA 82 | 7.5 baseband-communication 83 | 7.5.1 FFT 84 | 7.5.2 Viterbi 85 | 7.5.2 GPS/BD 86 | .... 87 | 88 | 8. Scala 89 | 8.1 scala-case-pattern 90 | 8.2 implicit 91 | 8.3 scala reflect 92 | 8.4 Macro 93 | 8.5 DataType 94 | 95 | 9. FunctionProgram 96 | 9.1 basic 97 | 9.3 advanced 98 | 9.3 usage 99 | 100 | a. Concepet 101 | a.1 Config 102 | a.2 Plugin 103 | -------------------------------------------------------------------------------- /binder/apt.txt: -------------------------------------------------------------------------------- 1 | openjdk-8-jre-headless 2 | graphviz 3 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCALA_VERSION=2.12.8 ALMOND_VERSION=0.5.0 4 | 5 | # Install coursier 6 | curl -Lo coursier https://git.io/coursier-cli 7 | chmod +x coursier 8 | 9 | # Install almond 10 | ./coursier bootstrap \ 11 | -r jitpack \ 12 | -i user -I user:sh.almond:scala-kernel-api_$SCALA_VERSION:$ALMOND_VERSION \ 13 | sh.almond:scala-kernel_$SCALA_VERSION:$ALMOND_VERSION \ 14 | --sources --default=true \ 15 | -o almond 16 | ./almond --install 17 | 18 | # Install required Jupyter/JupyterLab extensions 19 | jupyter labextension install @jupyterlab/plotly-extension 20 | 21 | # Install custom Javascript for solutions 22 | mkdir -p ~/.jupyter/custom 23 | cp source/custom.js ~/.jupyter/custom/custom.js 24 | -------------------------------------------------------------------------------- /exercises/.scalafix.conf: -------------------------------------------------------------------------------- 1 | rules = [ 2 | // RemoveUnused 3 | DisableSyntax 4 | LeakingImplicitClassVal 5 | NoValInForComprehension 6 | ProcedureSyntax 7 | ] 8 | 9 | // DisableSyntax.noVars = true 10 | DisableSyntax.noThrows = false 11 | DisableSyntax.noNulls = true 12 | DisableSyntax.noReturns = true 13 | DisableSyntax.noAsInstanceOf = true 14 | DisableSyntax.noIsInstanceOf = true 15 | DisableSyntax.noXml = true 16 | DisableSyntax.noFinalVal = true 17 | DisableSyntax.noFinalize = true 18 | DisableSyntax.noValPatterns = true 19 | -------------------------------------------------------------------------------- /exercises/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | ARG JDK_VERSION=11 4 | ARG MILL_VERSION=0.9.7 5 | ARG SCALA_VERSION=2.13.6 6 | ENV DEBIAN_FRONTEND=noninteractive 7 | 8 | # Install dependencies 9 | RUN apt-get update && apt-get install -y openjdk-$JDK_VERSION-jdk iverilog verilator python3-pip curl && pip3 install cocotb 10 | 11 | COPY . /tmp 12 | # Install mill 13 | RUN \ 14 | # echo "mill=${MILL_VERSION} jdk=$JDK_VERSION scala=$SCALA_VERSION" && \ 15 | curl -L -o /usr/local/bin/mill https://github.com/lihaoyi/mill/releases/download/$MILL_VERSION/$MILL_VERSION && \ 16 | chmod +x /usr/local/bin/mill && \ 17 | # touch build.sc && \ 18 | cd /tmp && \ 19 | mill -i resolve _ && \ 20 | rm -rf * 21 | 22 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/ApbArbiter.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | import spinal.lib.bus.amba3.apb._ 6 | 7 | class ApbArbiter( 8 | apbConfig: Apb3Config, 9 | numInputSlaves: Int 10 | ) extends Component { 11 | require(numInputSlaves > 0) 12 | 13 | val io = new Bundle { 14 | val en = in Bool () 15 | val masterOut = master(Apb3(apbConfig)) 16 | val slavesIn = Vec(slave(Apb3(apbConfig)), numInputSlaves) 17 | } 18 | 19 | io.masterOut.PSEL := 0 20 | io.masterOut.PENABLE := False 21 | io.masterOut.PADDR := 0 22 | io.masterOut.PWDATA := 0 23 | io.masterOut.PWRITE := False 24 | 25 | val slaveRegVec = Vec(Reg(Apb3(apbConfig)), numInputSlaves) 26 | for ((apbReg, apbIn) <- slaveRegVec.zip(io.slavesIn)) { 27 | apbReg.PREADY := False 28 | apbReg.PRDATA := 0 29 | apbReg << apbIn 30 | } 31 | 32 | val done = 33 | io.masterOut.PREADY && io.masterOut.PSEL === B(1) && io.masterOut.PENABLE; 34 | 35 | val apbReqBits = Bits(numInputSlaves bits) 36 | for ((slaveReg, idx) <- slaveRegVec.zipWithIndex) { 37 | when( 38 | io.slavesIn(idx).PSEL === B(1) && io.slavesIn(idx).PENABLE && !io 39 | .slavesIn(idx) 40 | .PREADY 41 | ) { 42 | apbReqBits(idx) := True 43 | }.otherwise { 44 | apbReqBits(idx) := False 45 | } 46 | } 47 | 48 | val priority = Reg(Bits(numInputSlaves bits)) init (1) 49 | val select = OHMasking.roundRobin(apbReqBits, priority) 50 | val selectIdx = OHToUInt(select) 51 | 52 | when(io.en) { 53 | val selectSlaveReg = slaveRegVec(selectIdx) 54 | when(select.orR) { 55 | io.masterOut << selectSlaveReg 56 | } 57 | 58 | when(done) { 59 | priority := priority.rotateLeft(1) 60 | } 61 | } 62 | } 63 | 64 | object ApbArbiter { 65 | def main(args: Array[String]): Unit = { 66 | val apbConfig = Apb3Config( 67 | addressWidth = 4, 68 | dataWidth = 32, 69 | selWidth = 1, 70 | useSlaveError = false 71 | ) 72 | val numInputSlaves = 4 73 | 74 | SpinalVerilog(new ApbArbiter(apbConfig, numInputSlaves)) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/ApbBridge.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.lib._ 6 | import spinal.sim._ 7 | import spinal.lib.bus.amba3.apb._ 8 | import spinal.lib.bus.misc._ 9 | 10 | class ApbBridge( 11 | val apbConfig: Apb3Config, 12 | val clkM: ClockDomain, 13 | val clkS: ClockDomain 14 | ) extends Component { 15 | val io = new Bundle { 16 | val apbM = master(Apb3(apbConfig)) 17 | val apbS = slave(Apb3(apbConfig)) 18 | } 19 | 20 | io.apbM.PADDR := io.apbS.PADDR; 21 | io.apbM.PWDATA := io.apbS.PWDATA; 22 | io.apbM.PWRITE := io.apbS.PWRITE; 23 | io.apbS.PRDATA := io.apbM.PRDATA; 24 | io.apbS.PSLVERROR := io.apbM.PSLVERROR; 25 | 26 | val doneM = clkM(RegInit(False)) 27 | 28 | val cdS = new ClockingArea(clkS) { 29 | val reqS = RegInit(False) 30 | val readyS = RegInit(False) 31 | val doneS = BufferCC(doneM, False) 32 | when(io.apbS.PSEL === B"1" && !io.apbS.PENABLE) { 33 | reqS := !reqS 34 | readyS := False 35 | }.elsewhen(io.apbS.PSEL === B"1" && io.apbS.PENABLE && doneS.edge(False)) { 36 | readyS := True 37 | }.otherwise { 38 | readyS := False 39 | } 40 | } 41 | io.apbS.PREADY := cdS.readyS 42 | 43 | val cdM = new ClockingArea(clkM) { 44 | val reqM = BufferCC(cdS.reqS, False) 45 | val pselM = Reg(Bits(apbConfig.selWidth bits)) init (B"0") 46 | val penableM = RegInit(False) 47 | when(reqM.edge(False)) { 48 | pselM := B"1" 49 | }.elsewhen(io.apbM.PREADY && pselM === B"1" && penableM) { 50 | doneM := !doneM 51 | penableM := False 52 | pselM := B"0" 53 | }.elsewhen(pselM === B"1") { 54 | penableM := True 55 | } 56 | } 57 | io.apbM.PSEL := cdM.pselM 58 | io.apbM.PENABLE := cdM.penableM 59 | } 60 | 61 | object ApbBridge { 62 | def main(args: Array[String]): Unit = { 63 | SpinalVerilog( 64 | new ApbBridge( 65 | apbConfig = Apb3Config( 66 | addressWidth = 4, 67 | dataWidth = 32, 68 | selWidth = 1, 69 | useSlaveError = true 70 | ), 71 | clkS = ClockDomain.external("clkS"), 72 | clkM = ClockDomain.external("clkM") 73 | ) 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/ApbBridgeSim.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.lib._ 6 | import spinal.sim._ 7 | import spinal.lib.bus.amba3.apb._ 8 | 9 | import scala.collection.mutable 10 | import scala.util.Random 11 | 12 | object ApbBridgeSim { 13 | def runSimulation(dut: ApbBridge): Unit = { 14 | SimTimeout(1000 * 1000) 15 | val writeQ = mutable.Queue[(Long, Long)]() 16 | val readQ = mutable.Queue[(Long, Long)]() 17 | 18 | // Fork a thread to manage the clock domains signals 19 | val clocksThread = fork { 20 | // Clear the clock domains' signals, to be sure the simulation captures their first edges. 21 | dut.clkM.fallingEdge() 22 | dut.clkS.fallingEdge() 23 | dut.clkM.deassertReset() 24 | dut.clkS.deassertReset() 25 | sleep(0) 26 | 27 | // Do the resets. 28 | dut.clkM.assertReset() 29 | dut.clkS.assertReset() 30 | sleep(5) 31 | dut.clkM.deassertReset() 32 | dut.clkS.deassertReset() 33 | sleep(1) 34 | 35 | // Forever, randomly toggle one of the clocks. 36 | // This will create asynchronous clocks without fixed frequencies. 37 | while (true) { 38 | if (Random.nextBoolean()) { 39 | dut.clkM.clockToggle() 40 | } else { 41 | dut.clkS.clockToggle() 42 | } 43 | sleep(1) 44 | } 45 | } 46 | 47 | sleep(0) 48 | dut.io.apbS.PENABLE #= false 49 | dut.io.apbS.PADDR #= 0 50 | dut.io.apbS.PWDATA #= 0 51 | dut.io.apbS.PRDATA #= 0 52 | dut.io.apbS.PWRITE #= false 53 | dut.io.apbS.PSEL #= 0 54 | dut.io.apbS.PREADY #= false 55 | dut.io.apbS.PSLVERROR #= false 56 | dut.io.apbM.PENABLE #= false 57 | dut.io.apbM.PADDR #= 0 58 | dut.io.apbM.PWDATA #= 0 59 | dut.io.apbM.PRDATA #= 0 60 | dut.io.apbM.PWRITE #= false 61 | dut.io.apbM.PSEL #= 0 62 | dut.io.apbM.PREADY #= false 63 | dut.io.apbM.PSLVERROR #= false 64 | sleep(6) 65 | 66 | var readMatch = 0 67 | val upStream = fork { 68 | while (true) { 69 | dut.io.apbS.PENABLE #= false 70 | dut.io.apbS.PADDR.randomize() 71 | dut.io.apbS.PWDATA.randomize() 72 | dut.io.apbS.PWRITE.randomize() 73 | dut.io.apbS.PSEL.randomize() 74 | //println(s"BEFORE pselS=${dut.io.apbS.PSEL.toInt}") 75 | dut.clkS.waitSampling() 76 | //assert(dut.io.apbS.PSEL.toInt == 1 && !dut.io.apbS.PENABLE.toBoolean) 77 | if (dut.io.apbS.PSEL.toInt == 1) { 78 | dut.io.apbS.PENABLE #= true 79 | //assert(dut.io.apbS.PENABLE.toBoolean) 80 | //println(s"pwriteS=${dut.io.apbS.PWRITE.toBoolean}") 81 | //assert(dut.io.apbS.PWRITE.toBoolean) 82 | if (dut.io.apbS.PWRITE.toBoolean) { 83 | println( 84 | s"writeS=${dut.io.apbS.PWDATA.toLong}, addrS=${dut.io.apbS.PADDR.toLong}" 85 | ) 86 | //dut.clkM.waitSampling() 87 | writeQ.enqueue( 88 | (dut.io.apbS.PADDR.toLong, dut.io.apbS.PWDATA.toLong) 89 | ) 90 | } 91 | } 92 | sleep(0) 93 | 94 | //assert(dut.io.apbS.PENABLE.toBoolean) 95 | //println(s"penableS=${dut.io.apbS.PENABLE.toBoolean}") 96 | if (dut.io.apbS.PENABLE.toBoolean) { 97 | assert(!dut.io.apbS.PREADY.toBoolean) 98 | println(s"BEFORE readyS=${dut.io.apbS.PREADY.toBoolean}") 99 | waitUntil(dut.io.apbS.PREADY.toBoolean) 100 | println(s"AFTER readyS=${dut.io.apbS.PREADY.toBoolean}") 101 | 102 | if (!dut.io.apbS.PWRITE.toBoolean) { 103 | val readTuple = readQ.dequeue() 104 | println( 105 | s"readS tuple=${readTuple}, addrS=${dut.io.apbS.PADDR.toLong}, readS=${dut.io.apbS.PRDATA.toLong}" 106 | ) 107 | assert( 108 | (dut.io.apbS.PADDR.toLong, dut.io.apbS.PRDATA.toLong) == readTuple 109 | ) 110 | readMatch += 1 111 | } 112 | //assert(dut.io.apbS.PENABLE.toBoolean) 113 | dut.clkS.waitSampling() 114 | dut.io.apbS.PSEL #= 0 115 | dut.io.apbS.PENABLE #= false 116 | } 117 | //println(s"pselS=${dut.io.apbS.PSEL.toInt}, penableS=${dut.io.apbS.PENABLE.toBoolean}") 118 | } 119 | } 120 | 121 | var writeMatch = 0 122 | val downStream = fork { 123 | while (true) { 124 | //for (i <- 0 until 2) { 125 | dut.io.apbM.PREADY #= false 126 | dut.io.apbM.PSLVERROR #= false 127 | waitUntil(dut.io.apbM.PSEL.toInt == 1) 128 | println(s"pselM=${dut.io.apbM.PSEL.toInt}") 129 | waitUntil(dut.io.apbM.PENABLE.toBoolean) 130 | println(s"penableM=${dut.io.apbM.PENABLE.toBoolean}") 131 | dut.io.apbM.PRDATA.randomize() 132 | dut.clkM.waitSampling() 133 | if (dut.io.apbM.PENABLE.toBoolean) { 134 | //assert(dut.io.apbS.PENABLE.toBoolean) 135 | //assert(dut.io.apbM.PENABLE.toBoolean) 136 | assert(!dut.io.apbM.PREADY.toBoolean) 137 | //assert(dut.io.apbM.PWRITE.toBoolean) 138 | if (!dut.io.apbM.PWRITE.toBoolean) { 139 | println( 140 | s"readM=${dut.io.apbM.PRDATA.toLong}, addrM=${dut.io.apbS.PADDR.toLong}" 141 | ) 142 | readQ.enqueue((dut.io.apbS.PADDR.toLong, dut.io.apbM.PRDATA.toLong)) 143 | } else { 144 | val writeTuple = writeQ.dequeue() 145 | println( 146 | s"writeM tuple=${writeTuple}, addrM=${dut.io.apbM.PADDR.toLong}, writeM=${dut.io.apbM.PWDATA.toLong}" 147 | ) 148 | assert( 149 | ( 150 | dut.io.apbM.PADDR.toLong, 151 | dut.io.apbM.PWDATA.toLong 152 | ) == writeTuple 153 | ) 154 | writeMatch += 1 155 | } 156 | //println(s"BEFORE preadyM=${dut.io.apbM.PREADY.toBoolean}") 157 | dut.io.apbM.PREADY #= true 158 | dut.clkM.waitSampling() 159 | println(s"AFTER preadyM=${dut.io.apbM.PREADY.toBoolean}") 160 | assert( 161 | dut.io.apbM.PREADY.toBoolean && dut.io.apbM.PENABLE.toBoolean && dut.io.apbM.PSEL.toInt == 1 162 | ) 163 | dut.io.apbM.PREADY #= false 164 | waitUntil( 165 | !dut.io.apbM.PENABLE.toBoolean && dut.io.apbM.PSEL.toInt == 0 166 | ) 167 | } 168 | } 169 | } 170 | 171 | waitUntil(writeMatch > 1000 && readMatch > 1000) 172 | simSuccess() 173 | } 174 | 175 | def main(args: Array[String]): Unit = { 176 | // Compile the Component for the simulator. 177 | val compiled = SimConfig.withWave.allOptimisation.compile { 178 | val dut = new ApbBridge( 179 | apbConfig = Apb3Config( 180 | addressWidth = 4, 181 | dataWidth = 32, 182 | selWidth = 1, 183 | useSlaveError = true 184 | ), 185 | clkS = ClockDomain.external("clkS"), 186 | clkM = ClockDomain.external("clkM") 187 | ) 188 | dut.io.apbM.simPublic() 189 | dut.io.apbS.simPublic() 190 | dut 191 | } 192 | 193 | // Run the simulation. 194 | compiled.doSim(runSimulation(_)) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/Booth.scala: -------------------------------------------------------------------------------- 1 | // https://www.cnblogs.com/hiramlee0534/p/3440168.html 2 | 3 | package exercises 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.sim._ 9 | 10 | class Booth(width: Int) extends Component { 11 | require(width > 1) 12 | 13 | val io = new Bundle { 14 | val load = in Bool () 15 | val multiplicand = in SInt (width bits) 16 | val multipler = in SInt (width bits) 17 | val ready = out Bool () 18 | val product = out SInt (2 * width bits) 19 | } 20 | 21 | val buf = Reg(SInt(2 * width + 1 bits)) init (0) 22 | val upperPart = buf(2 * width downto width + 1) 23 | val lowerPart = buf(width downto 0) 24 | val cnt = CounterFreeRun(stateCount = width + 2) 25 | 26 | buf.simPublic() 27 | upperPart.simPublic() 28 | lowerPart.simPublic() 29 | cnt.value.simPublic() 30 | 31 | when(io.load) { 32 | buf := S(0, width bits) @@ io.multipler @@ S(0, 1 bit) 33 | }.elsewhen(!io.ready) { 34 | switch(buf(1 downto 0).asBits) { 35 | is(0, 3) { 36 | buf := (buf >> 1).resized 37 | } 38 | is(1) { 39 | buf := (((upperPart + io.multiplicand) @@ lowerPart) >> 1).resized 40 | } 41 | is(2) { 42 | buf := (((upperPart - io.multiplicand) @@ lowerPart) >> 1).resized 43 | } 44 | // is (3) { 45 | // buf := (buf >> 1).resized 46 | // } 47 | } 48 | }.otherwise { 49 | cnt.clear() 50 | } 51 | 52 | io.ready := cnt.willOverflow 53 | io.product := buf >> 1 54 | } 55 | 56 | object Booth { 57 | def main(args: Array[String]): Unit = { 58 | SpinalSystemVerilog(new Booth(8)) 59 | } 60 | } 61 | 62 | object BoothSim extends App { 63 | SimConfig.withWave 64 | // .withConfig(SpinalConfig( 65 | // defaultClockDomainFrequency = FixedFrequency(100 MHz), 66 | // defaultConfigForClockDomains = ClockDomainConfig(resetKind = SYNC))) 67 | .compile(new Booth(6)) 68 | .doSim { dut => 69 | SimTimeout(100) 70 | 71 | dut.clockDomain.forkStimulus(2) 72 | 73 | val multiplicand = 4 74 | val multipler = -3 75 | 76 | dut.io.multiplicand #= multiplicand 77 | dut.io.multipler #= multipler 78 | dut.io.load #= true 79 | dut.clockDomain.waitSampling() 80 | dut.io.load #= false 81 | //dut.clockDomain.waitSampling() 82 | while (true) { 83 | dut.clockDomain.waitSampling() 84 | println(s""" 85 | C=${dut.cnt.value.toInt}, 86 | B=${dut.buf.toInt}, 87 | U=${dut.upperPart.toInt}, 88 | L=${dut.lowerPart.toInt}, 89 | P=${dut.io.product.toInt}, 90 | ready=${dut.io.ready.toBoolean}""") 91 | 92 | if (dut.io.ready.toBoolean == true) { 93 | assert(dut.io.product.toInt == multiplicand * multipler) 94 | simSuccess() 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/FifoCC.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | 6 | class FifoCC[T <: Data]( 7 | dataType: HardType[T], 8 | val depth: Int, 9 | val pushClock: ClockDomain, 10 | val popClock: ClockDomain 11 | ) extends Component { 12 | require( 13 | isPow2(depth) & depth >= 16, 14 | "FIFO depth must be power of 2 and larger than 16" 15 | ) 16 | 17 | val io = new Bundle { 18 | val wData = in(dataType()) 19 | val wEn = in Bool () 20 | val wFull = out Bool () 21 | val rData = out(dataType()) 22 | val rEn = in Bool () 23 | val rEmpty = out Bool () 24 | } 25 | 26 | val ADDR_WIDTH = log2Up(depth + 1) 27 | val mem = Mem(dataType, depth) 28 | 29 | val popToPushGray = Bits(ADDR_WIDTH bits) addTag (crossClockDomain) 30 | val pushToPopGray = Bits(ADDR_WIDTH bits) addTag (crossClockDomain) 31 | 32 | val pushArea = new ClockingArea(pushClock) { 33 | val wAddr = Reg(UInt(ADDR_WIDTH bits)) init (0) 34 | val wAddrInc = io.wEn && !io.wFull 35 | mem.write( 36 | enable = wAddrInc, 37 | address = wAddr.resized, 38 | data = io.wData 39 | ) 40 | wAddr := wAddr + wAddrInc.asUInt 41 | 42 | val wAddrGray = toGray(wAddr) 43 | val rAddrGrayInPushArea = 44 | BufferCC(popToPushGray, init = B(0, ADDR_WIDTH bits)) 45 | // val rAddrInPushArea = fromGray(rAddrGrayInPushArea) 46 | 47 | io.wFull := (wAddrGray( 48 | ADDR_WIDTH - 1 downto ADDR_WIDTH - 2 49 | ) === ~rAddrGrayInPushArea(ADDR_WIDTH - 1 downto ADDR_WIDTH - 2) 50 | && wAddrGray(ADDR_WIDTH - 3 downto 0) === rAddrGrayInPushArea( 51 | ADDR_WIDTH - 3 downto 0 52 | )) 53 | } 54 | 55 | val popArea = new ClockingArea(popClock) { 56 | val rAddr = Reg(UInt(ADDR_WIDTH bits)) init (0) 57 | val rAddrFire = io.rEn && !io.rEmpty 58 | // io.rData := mem.readSync(rAddr.resized, enable = rAddrFire, clockCrossing = true) 59 | io.rData := mem.readAsync(rAddr.resized) 60 | rAddr := rAddr + rAddrFire.asUInt 61 | 62 | val rAddrGray = toGray(rAddr) 63 | val wAddrGrayInPopArea = 64 | BufferCC(pushToPopGray, init = B(0, ADDR_WIDTH bits)) 65 | // val wAddrInPopArea = fromGray(wAddrGrayInPopArea) 66 | 67 | io.rEmpty := rAddrGray === wAddrGrayInPopArea 68 | } 69 | 70 | popToPushGray := popArea.rAddrGray 71 | pushToPopGray := pushArea.wAddrGray 72 | } 73 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/FifoCCSim.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.core.sim._ 5 | import spinal.lib._ 6 | import spinal.sim._ 7 | 8 | import scala.collection.mutable 9 | import scala.util.Random 10 | 11 | object FifoCCSim { 12 | def runSimulation[T <: BitVector](dut: FifoCC[T]): Unit = { 13 | SimTimeout(1000 * 1000) 14 | val queueModel = mutable.Queue[Long]() 15 | 16 | dut.io.wEn #= false 17 | dut.io.rEn #= false 18 | 19 | // Create asynchronous clocks 20 | dut.pushClock.forkStimulus(2) 21 | sleep(17) 22 | dut.popClock.forkStimulus(5) 23 | sleep(100) 24 | 25 | // Do the resets 26 | dut.pushClock.assertReset() 27 | dut.popClock.assertReset() 28 | sleep(10) 29 | dut.pushClock.deassertReset() 30 | dut.popClock.deassertReset() 31 | sleep(100) 32 | 33 | // Push data randomly, and fill the queueModel with pushed transactions 34 | val pushThread = fork { 35 | while (true) { 36 | dut.io.wEn.randomize() 37 | dut.io.wData.randomize() 38 | dut.pushClock.waitSampling() 39 | 40 | if (dut.io.wEn.toBoolean && !dut.io.wFull.toBoolean) { 41 | queueModel.enqueue(dut.io.wData.toLong) 42 | } 43 | dut.io.wEn #= false 44 | } 45 | } 46 | 47 | var readMatch = 0 48 | // Pop data randomly, and check that it match with the queueModel 49 | val popThread = fork { 50 | dut.pushClock.waitSampling(20) 51 | while (true) { 52 | dut.io.rEn.randomize() 53 | dut.popClock.waitSampling() 54 | 55 | if (dut.io.rEn.toBoolean && !dut.io.rEmpty.toBoolean) { 56 | assert(dut.io.rData.toLong == queueModel.dequeue()) 57 | readMatch += 1 58 | } 59 | } 60 | } 61 | 62 | waitUntil(readMatch > 50000) 63 | simSuccess() 64 | } 65 | 66 | def main(args: Array[String]): Unit = { 67 | // Compile the Component for the simulator 68 | val compiled = SimConfig.withWave.allOptimisation.compile { 69 | val dut = new FifoCC( 70 | dataType = Bits(32 bits), 71 | depth = 32, 72 | pushClock = ClockDomain.external("clkPush"), 73 | popClock = ClockDomain.external("clkPop") 74 | ) 75 | // dut.pushArea.wAddr.simPublic() 76 | // dut.pushArea.rAddrInPushArea.simPublic() 77 | // dut.popArea.rAddr.simPublic() 78 | // dut.popArea.wAddrInPopArea.simPublic() 79 | dut 80 | } 81 | 82 | // Run the simulation. 83 | compiled.doSim(runSimulation(_)) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/HandShakePipe.scala: -------------------------------------------------------------------------------- 1 | // https://www.itdev.co.uk/blog/pipelining-axi-buses-registered-ready-signals 2 | // http://fpgacpu.ca/fpga/Pipeline_Skid_Buffer.html 3 | 4 | package exercises 5 | 6 | import spinal.core._ 7 | import spinal.lib._ 8 | import spinal.lib.fsm._ 9 | 10 | class HandShakePipe(width: Int) extends Component { 11 | val io = new Bundle { 12 | val input = new Bundle { 13 | val valid = in Bool () 14 | val payload = in UInt (width bits) 15 | val ready = out Bool () 16 | } 17 | 18 | val output = new Bundle { 19 | val valid = out Bool () 20 | val payload = out UInt (width bits) 21 | val ready = in Bool () 22 | } 23 | } 24 | } 25 | 26 | class M2SHandShakePipe( 27 | width: Int, 28 | noBubble: Boolean = true 29 | ) extends HandShakePipe(width) { 30 | val doutReg = Reg(UInt(width bits)) init (0) 31 | val validReg = Reg(Bool) init (False) 32 | io.output.payload := doutReg 33 | io.output.valid := validReg 34 | 35 | if (noBubble) { 36 | when(io.input.ready) { 37 | doutReg := io.input.payload 38 | validReg := io.input.valid 39 | } 40 | 41 | io.input.ready := io.output.ready || !validReg 42 | } else { 43 | when(io.input.ready) { 44 | doutReg := io.input.payload 45 | validReg := io.input.valid 46 | } 47 | 48 | io.input.ready := io.output.ready 49 | } 50 | } 51 | 52 | class S2MHandShakePipe( 53 | width: Int, 54 | noBubble: Boolean = true 55 | ) extends HandShakePipe(width) { 56 | val readyReg = RegNext(io.output.ready) init (False) 57 | val doutReg = Reg(UInt(width bits)) init (0) 58 | val validReg = Reg(Bool) init (False) 59 | 60 | if (noBubble) { 61 | io.output.valid := io.input.valid || validReg 62 | io.output.payload := validReg ? doutReg | io.input.payload 63 | 64 | io.input.ready := ~validReg 65 | 66 | when(io.output.ready) { 67 | validReg := False 68 | } 69 | 70 | when(io.input.ready && ~io.output.ready) { 71 | doutReg := io.input.payload 72 | validReg := io.input.valid 73 | } 74 | } else { 75 | io.input.ready := readyReg 76 | when(io.output.ready && !readyReg) { 77 | io.output.payload := doutReg 78 | io.output.valid := validReg 79 | }.elsewhen(io.output.ready && readyReg) { 80 | io.output.payload := io.input.payload 81 | io.output.valid := io.input.valid 82 | }.elsewhen(!io.output.ready && readyReg) { 83 | io.output.payload := io.input.payload 84 | io.output.valid := io.input.valid 85 | }.otherwise { // !io.output.ready && !readyReg 86 | io.output.payload := io.input.payload 87 | io.output.valid := io.input.valid 88 | } 89 | } 90 | } 91 | 92 | class BothHandShakePipe( 93 | width: Int 94 | ) extends HandShakePipe(width) { 95 | val primDataReg = Reg(UInt(width bits)) init (0) 96 | val primVldReg = Reg(Bool) init (False) 97 | 98 | val sideDataReg = Reg(UInt(width bits)) init (0) 99 | val sideVldReg = Reg(Bool) init (False) 100 | 101 | io.input.ready := ~sideVldReg 102 | io.output.valid := primVldReg || sideVldReg 103 | io.output.payload := sideVldReg ? sideDataReg | primDataReg 104 | 105 | when(io.output.ready) { 106 | sideVldReg := False 107 | } 108 | 109 | when(io.input.ready) { 110 | primDataReg := io.input.payload 111 | primVldReg := io.input.valid 112 | 113 | when(~io.output.ready) { 114 | sideDataReg := primDataReg 115 | sideVldReg := primVldReg 116 | } 117 | } 118 | } 119 | 120 | class BothHandShakePipe2( 121 | width: Int 122 | ) extends HandShakePipe(width) { 123 | val firstDataReg = Reg(UInt(width bits)) init (0) 124 | val firstVldReg = Reg(Bool) init (False) 125 | 126 | val interStream = Stream(UInt(width bits)) 127 | 128 | val secondDataReg = Reg(UInt(width bits)) init (0) 129 | val secondVldReg = Reg(Bool) init (False) 130 | 131 | val s2mArea = new Area { 132 | io.input.ready := ~firstVldReg 133 | interStream.valid := io.input.valid || firstVldReg 134 | interStream.payload := firstVldReg ? firstDataReg | io.input.payload 135 | 136 | when(interStream.ready) { 137 | firstVldReg := False 138 | } 139 | 140 | when(io.input.ready && ~interStream.ready) { 141 | firstDataReg := io.input.payload 142 | firstVldReg := io.input.valid 143 | } 144 | } 145 | 146 | val m2sArea = new Area { 147 | io.output.valid := secondVldReg 148 | io.output.payload := secondDataReg 149 | interStream.ready := io.output.ready || ~secondVldReg 150 | 151 | when(interStream.ready) { 152 | secondDataReg := interStream.payload 153 | secondVldReg := interStream.valid 154 | } 155 | } 156 | } 157 | 158 | class BothHandShakePipe3( 159 | width: Int 160 | ) extends HandShakePipe(width) { 161 | 162 | object SkidBufState extends SpinalEnum { 163 | val EMPTY, BUSY, FULL = newElement() 164 | } 165 | 166 | val load = Bool() 167 | val unload = Bool() 168 | val flow = Bool() 169 | val fill = Bool() 170 | val flush = Bool() 171 | 172 | val insert = io.input.valid && io.input.ready 173 | val remove = io.output.valid && io.output.ready 174 | 175 | val skidBufferReg = Reg(UInt(width bits)) init (0) 176 | val outBufferReg = Reg(UInt(width bits)) init (0) 177 | 178 | val state = Reg(SkidBufState) init (SkidBufState.EMPTY) 179 | 180 | load := state === SkidBufState.EMPTY && insert && !remove 181 | unload := state === SkidBufState.BUSY && !insert && remove 182 | flow := state === SkidBufState.BUSY && insert && remove 183 | fill := state === SkidBufState.BUSY && insert && !remove 184 | flush := state === SkidBufState.FULL && !insert && remove 185 | 186 | when(flush) { 187 | outBufferReg := skidBufferReg 188 | } elsewhen (load || flow) { 189 | outBufferReg := io.input.payload 190 | } 191 | when(fill) { 192 | skidBufferReg := io.input.payload 193 | } 194 | 195 | val nextState = SkidBufState() 196 | nextState := state 197 | switch(state) { 198 | is(SkidBufState.EMPTY) { 199 | when(load) { 200 | nextState := SkidBufState.BUSY 201 | } 202 | } 203 | is(SkidBufState.BUSY) { 204 | when(fill) { 205 | nextState := SkidBufState.FULL 206 | } elsewhen (unload) { 207 | nextState := SkidBufState.EMPTY 208 | } 209 | } 210 | is(SkidBufState.FULL) { 211 | when(flush) { 212 | nextState := SkidBufState.BUSY 213 | } 214 | } 215 | } 216 | state := nextState 217 | 218 | io.input.ready := state =/= SkidBufState.FULL 219 | io.output.valid := state =/= SkidBufState.EMPTY 220 | io.output.payload := outBufferReg 221 | } 222 | -------------------------------------------------------------------------------- /exercises/Examples/src/main/scala/exercises/WeightedArbiter.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import spinal.core._ 4 | import spinal.lib._ 5 | 6 | class WeightedArbiter(numReq: Int, weightWidth: Int = 8) extends Component { 7 | val io = new Bundle { 8 | val clear = in Bool () 9 | val weights = in Vec (UInt(weightWidth bits), numReq) 10 | val reqs = in Bits (numReq bits) 11 | val grant = out Bits (numReq bits) 12 | } 13 | 14 | def isOneHot(oneHot: Bits) = new Composite(oneHot) { 15 | val wordWidth = oneHot.getWidth 16 | val bitsVec = Vec(Bool(), wordWidth) 17 | bitsVec(0) := oneHot(0) 18 | for (idx <- 1 until wordWidth) { 19 | bitsVec(idx) := bitsVec(idx - 1) ^ oneHot(idx) 20 | } 21 | 22 | val xorRslt = bitsVec.asBits 23 | val shouldBeAllOnes = (xorRslt | ~oneHot) 24 | val isNotAllZeros = oneHot.orR 25 | val isAllOnes = shouldBeAllOnes.andR 26 | val result = isAllOnes && isNotAllZeros 27 | }.result 28 | 29 | def priorityArbitrate( 30 | reqs: Bits, 31 | reqBase: Bits 32 | ) = new Composite(this) { 33 | val reqMask = (reqs.asUInt - reqBase.asUInt).asBits 34 | val grant = reqs & ~reqMask 35 | }.grant 36 | 37 | def updateWeights( 38 | initialWeightVec: Vec[UInt], 39 | weightVec: Vec[UInt], 40 | grant: Bits, 41 | reload: Bool, 42 | numReq: Int, 43 | weightWidth: Int 44 | ) = new Area { 45 | val grantIdx = OHToUInt(grant) 46 | val updateWeight = grant.orR // grant is zero or not 47 | val weights = reload ? initialWeightVec | weightVec 48 | 49 | when(reload) { 50 | weightVecReg := initialWeightVec 51 | } 52 | when(updateWeight) { 53 | weightVec(grantIdx) := weights(grantIdx) - 1 54 | 55 | assert( 56 | assertion = weights(grantIdx) > 0, 57 | message = "granted request should have weight > 0", 58 | severity = ERROR 59 | ) 60 | } 61 | } 62 | 63 | def updateLastGrant( 64 | lastGrant: Bits, 65 | clear: Bool, 66 | update: Bool, 67 | grant: Bits 68 | ) = new Area { 69 | when(clear) { 70 | lastGrant := 1 71 | } elsewhen (update) { 72 | when(grant.msb === True) { // Finish one round 73 | lastGrant := 1 74 | } otherwise { 75 | lastGrant := grant 76 | } 77 | 78 | val checkOH = isOneHot(grant) 79 | // report(L"grant=${grant}") 80 | assert( 81 | assertion = checkOH, 82 | message = L"grant=${grant} should be one hot", 83 | severity = ERROR 84 | ) 85 | } 86 | } 87 | 88 | val initialWeightVecReg = Reg(Vec(UInt(weightWidth bits), numReq)) 89 | val initialWeightMaskReg = Reg(Bits(numReq bits)) 90 | when(io.clear) { 91 | initialWeightVecReg := io.weights 92 | initialWeightMaskReg := io.weights.map(w => w.orR).asBits() 93 | } 94 | 95 | val reload = Bool() 96 | val weightVecReg = Reg(Vec(UInt(weightWidth bits), numReq)) 97 | val weightMask = weightVecReg.map(w => w.orR).asBits() 98 | updateWeights(io.weights, weightVecReg, io.grant, reload, numReq, weightWidth) 99 | 100 | val lastGrantReg = Reg(Bits(numReq bits)) 101 | 102 | val roundRobinMask = ~(lastGrantReg | (lastGrantReg.asUInt - 1).asBits) 103 | val maskedReqs = io.reqs & weightMask & roundRobinMask 104 | val hasMaskedReqs = maskedReqs.orR 105 | val maskedGrant = priorityArbitrate(maskedReqs, lastGrantReg) 106 | reload := io.clear | (io.reqs.orR & !hasMaskedReqs) 107 | 108 | val weightedReqs = io.reqs & initialWeightMaskReg 109 | val hasReqs = weightedReqs.orR 110 | val weightedReqBase = B(1, numReq bits) 111 | val unmaskedGrant = priorityArbitrate(weightedReqs, weightedReqBase) 112 | 113 | val noMaskedReqs = B(numReq bits, default -> !hasMaskedReqs) 114 | io.grant := (noMaskedReqs & unmaskedGrant) | maskedGrant 115 | 116 | updateLastGrant(lastGrantReg, io.clear, hasReqs, io.grant) 117 | } 118 | 119 | object WeightedArbiter { 120 | def main(args: Array[String]): Unit = { 121 | SpinalVerilog(new WeightedArbiter(numReq = 4)).printPruned() 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /exercises/Examples/test/src/scala/exercises/ApbArbiterTest.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.lib.bus.amba3.apb._ 9 | import spinal.sim._ 10 | 11 | class ApbArbiterTest extends AnyFunSuite { 12 | test("apb arbiter test") { 13 | val apbConfig = Apb3Config( 14 | addressWidth = 4, 15 | dataWidth = 32, 16 | selWidth = 1, 17 | useSlaveError = false 18 | ) 19 | val numInputSlaves = 8 20 | SimConfig.withWave.compile { 21 | val dut = new ApbArbiter(apbConfig, numInputSlaves) 22 | dut.select.simPublic() 23 | dut.priority.simPublic() 24 | dut.apbReqBits.simPublic() 25 | dut.selectIdx.simPublic() 26 | dut 27 | } 28 | .doSim { dut => 29 | SimTimeout(500) 30 | 31 | dut.clockDomain.forkStimulus(2) 32 | 33 | val apbMasters = fork { 34 | for (idx <- 0 until numInputSlaves) { 35 | dut.io.slavesIn(idx).PSEL #= 1 36 | dut.io.slavesIn(idx).PENABLE #= true 37 | dut.io.slavesIn(idx).PADDR #= idx 38 | dut.io.slavesIn(idx).PWDATA #= 0 39 | dut.io.slavesIn(idx).PWRITE #= false 40 | dut.io.slavesIn(idx).PRDATA #= numInputSlaves 41 | dut.io.slavesIn(idx).PREADY #= false 42 | } 43 | while (true) { 44 | dut.clockDomain.waitSampling() 45 | for (idx <- 0 until numInputSlaves) { 46 | if (dut.io.slavesIn(idx).PREADY.toBoolean) { 47 | dut.io.slavesIn(idx).PSEL #= 0 48 | dut.io.slavesIn(idx).PENABLE #= false 49 | assert( 50 | dut.io.slavesIn(idx).PRDATA.toLong == idx + 1, 51 | s"PRDATA[${idx}]${dut.io.slavesIn(idx).PRDATA.toLong} != ${idx} + 1" 52 | ) 53 | } 54 | } 55 | } 56 | } 57 | 58 | dut.io.en #= false 59 | dut.io.masterOut.PREADY #= false 60 | dut.clockDomain.waitSampling(2) 61 | dut.io.en #= true 62 | 63 | for (round <- 0 until numInputSlaves) { 64 | dut.clockDomain.waitSampling() 65 | dut.io.masterOut.PREADY #= false 66 | 67 | dut.clockDomain.waitSampling(2) 68 | println(s""" 69 | round=${round} 70 | reqs=${dut.apbReqBits.toInt}, 71 | select=${dut.select.toInt}, 72 | selectIdx=${dut.selectIdx.toInt}, 73 | priority=${dut.priority.toInt}, 74 | """) 75 | 76 | dut.io.masterOut.PREADY #= true 77 | dut.io.masterOut.PRDATA #= dut.selectIdx.toInt + 1 78 | } 79 | 80 | dut.clockDomain.waitSampling() 81 | dut.io.masterOut.PRDATA #= numInputSlaves 82 | dut.io.masterOut.PREADY #= false 83 | dut.clockDomain.waitSampling() 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /exercises/Examples/test/src/scala/exercises/ApbBridgeTest.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.lib.bus.amba3.apb._ 9 | import spinal.sim._ 10 | 11 | class ApbBridgeTest extends AnyFunSuite { 12 | test("async bridge test") { 13 | ApbBridgeSim.main(null) 14 | } 15 | 16 | test("sync bridge test") { 17 | val cd = ClockDomain.external("clk") 18 | val compiled = SimConfig.withWave.compile( 19 | rtl = new ApbBridge( 20 | apbConfig = Apb3Config( 21 | addressWidth = 4, 22 | dataWidth = 32, 23 | selWidth = 1, 24 | useSlaveError = true 25 | ), 26 | clkS = cd, 27 | clkM = cd 28 | ) 29 | ) 30 | 31 | // Run the simulation 32 | compiled.doSim(ApbBridgeSim.runSimulation(_)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /exercises/Examples/test/src/scala/exercises/FifoCCTest.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.sim._ 9 | 10 | class FifoCCTest extends AnyFunSuite { 11 | test("async fifo test") { 12 | FifoCCSim.main(null) 13 | } 14 | 15 | test("sync fifo test") { 16 | val cd = ClockDomain.external("clk") 17 | val compiled = SimConfig.withWave.compile( 18 | rtl = new FifoCC( 19 | dataType = Bits(32 bits), 20 | depth = 16, 21 | pushClock = cd, 22 | popClock = cd 23 | ) 24 | ) 25 | 26 | // Run the simulation 27 | compiled.doSim(FifoCCSim.runSimulation(_)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/Examples/test/src/scala/exercises/HandShakePipeTest.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.lib.bus.amba3.apb._ 9 | import spinal.sim._ 10 | 11 | class HandShakePipeTest extends AnyFunSuite { 12 | // implicit class StreamExtend(stream: Stream[UInt]) { 13 | // def init() { 14 | // stream.valid #= false 15 | // stream.payload #= 0 16 | // stream.ready #= true 17 | // } 18 | 19 | // def simPush(payload: Int)(cd: ClockDomain): Unit ={ 20 | // stream.valid #= true 21 | // stream.payload #= payload 22 | // cd.waitSampling() 23 | // while (!stream.ready.toBoolean) { 24 | // cd.waitSampling() 25 | // } 26 | // stream.valid #= false 27 | // stream.payload #= 0 28 | // } 29 | 30 | // def monitor(queue: Queue[Int])(cd: ClockDomain) { 31 | // while (true) { 32 | // cd.waitSampling() 33 | // if (stream.valid.toBoolean && stream.ready.toBoolean) { 34 | // queue.enqueue(stream.payload.toInt) 35 | // } 36 | // } 37 | // } 38 | // } 39 | 40 | // class StreamTopTB(width: Int) extends StreamTop(width) { 41 | // def init() { 42 | // io.sin.init() 43 | // io.sout.init() 44 | // } 45 | 46 | // def simPush(payload: Int) { 47 | // io.sin.simPush(payload)(clockDomain) 48 | // } 49 | 50 | // val queue = Queue[Int]() 51 | // def monitor() { 52 | // io.sout.monitor(queue)(clockDomain) 53 | // } 54 | // } 55 | 56 | class PipeStream(width: Int) extends Component { 57 | val io = new Bundle { 58 | val sin = slave Stream(UInt(width bits)) 59 | val sout = master Stream(UInt(width bits)) 60 | } 61 | } 62 | 63 | class M2SPipeStream(width: Int) extends PipeStream(width) { 64 | io.sout << io.sin.m2sPipe() 65 | } 66 | 67 | class S2MPipeStream(width: Int) extends PipeStream(width) { 68 | io.sout << io.sin.s2mPipe() 69 | } 70 | 71 | class BothPipeStream(width: Int) extends PipeStream(width) { 72 | io.sout << io.sin.m2sPipe().s2mPipe() 73 | } 74 | 75 | class BothPipeStream2(width: Int) extends PipeStream(width) { 76 | io.sout << io.sin.s2mPipe().m2sPipe() 77 | } 78 | 79 | abstract class PipeTB(width: Int) extends Component { 80 | val pipe: HandShakePipe 81 | val stream: PipeStream 82 | } 83 | 84 | class M2SPipeTB(width: Int) extends PipeTB(width) { 85 | override val pipe = new M2SHandShakePipe(width) 86 | override val stream = new M2SPipeStream(width) 87 | 88 | pipe.io.simPublic() 89 | stream.io.simPublic() 90 | } 91 | 92 | class S2MPipeTB(width: Int) extends PipeTB(width) { 93 | override val pipe = new S2MHandShakePipe(width) 94 | override val stream = new S2MPipeStream(width) 95 | 96 | pipe.io.simPublic() 97 | stream.io.simPublic() 98 | 99 | pipe.readyReg.simPublic() 100 | pipe.validReg.simPublic() 101 | pipe.doutReg.simPublic() 102 | } 103 | 104 | class BothPipeTB(width: Int) extends PipeTB(width) { 105 | override val pipe = new BothHandShakePipe(width) 106 | override val stream = new BothPipeStream(width) 107 | 108 | pipe.io.simPublic() 109 | stream.io.simPublic() 110 | } 111 | 112 | class BothPipe2TB(width: Int) extends PipeTB(width) { 113 | override val pipe = new BothHandShakePipe2(width) 114 | override val stream = new BothPipeStream2(width) 115 | 116 | pipe.io.simPublic() 117 | stream.io.simPublic() 118 | } 119 | 120 | class BothPipe3TB(width: Int) extends PipeTB(width) { 121 | override val pipe = new BothHandShakePipe3(width) 122 | override val stream = new BothPipeStream2(width) 123 | 124 | pipe.io.simPublic() 125 | stream.io.simPublic() 126 | } 127 | 128 | def simTest(dut: PipeTB): Unit = { 129 | SimTimeout(1000) 130 | dut.clockDomain.forkStimulus(2) 131 | 132 | dut.pipe.io.input.valid #= false 133 | dut.pipe.io.input.payload #= 0 134 | dut.pipe.io.input.ready #= true 135 | dut.pipe.io.output.valid #= false 136 | dut.pipe.io.output.payload #= 0 137 | dut.pipe.io.output.ready #= true 138 | dut.stream.io.sin.valid #= false 139 | dut.stream.io.sin.payload #= 0 140 | dut.stream.io.sin.ready #= true 141 | dut.stream.io.sout.valid #= false 142 | dut.stream.io.sout.payload #= 0 143 | dut.stream.io.sout.ready #= true 144 | sleep(0) 145 | 146 | val rand = new scala.util.Random() 147 | var din = 0 148 | 149 | val sender = fork { 150 | while (true) { 151 | dut.pipe.io.input.valid #= true 152 | dut.pipe.io.input.payload #= din 153 | dut.stream.io.sin.valid #= true 154 | dut.stream.io.sin.payload #= din 155 | dut.clockDomain.waitSampling() 156 | assert( 157 | dut.pipe.io.input.ready.toBoolean == dut.stream.io.sin.ready.toBoolean, 158 | s"pipe ri=${dut.pipe.io.input.ready.toBoolean} not match stream ri=${dut.stream.io.sin.ready.toBoolean}" 159 | ) 160 | if (dut.pipe.io.input.valid.toBoolean && dut.pipe.io.input.ready.toBoolean) { 161 | din += 1 162 | } 163 | } 164 | } 165 | 166 | var matchCnt = 0 167 | val receiver = fork { 168 | while (true) { 169 | val randBool = rand.nextBoolean() 170 | dut.pipe.io.output.ready #= randBool 171 | dut.stream.io.sout.ready #= randBool 172 | dut.clockDomain.waitSampling() 173 | assert( 174 | dut.pipe.io.output.valid.toBoolean == dut.stream.io.sout.valid.toBoolean, 175 | s"pipe vo=${dut.pipe.io.output.valid.toBoolean} not match stream vo=${dut.stream.io.sout.valid.toBoolean}" 176 | ) 177 | if (dut.pipe.io.output.valid.toBoolean && dut.pipe.io.output.ready.toBoolean) { 178 | assert( 179 | dut.pipe.io.output.payload.toInt == dut.stream.io.sout.payload.toInt, 180 | s"pipe dout=${dut.pipe.io.output.payload.toInt} not match stream dout=${dut.stream.io.sout.payload.toInt}" 181 | ) 182 | matchCnt += 1 183 | if (matchCnt > 100) { 184 | simSuccess() 185 | } 186 | } 187 | } 188 | } 189 | 190 | while (true) { 191 | dut.clockDomain.waitSampling() 192 | // pipe_vr=${dut.pipe.validReg.toBoolean}, 193 | // pipe_rr=${dut.pipe.readyReg.toBoolean}, 194 | // pipe_dr=${dut.pipe.doutReg.toInt}, 195 | println(s""" 196 | pipe_din=${dut.pipe.io.input.payload.toInt}, 197 | pipe_dout=${dut.pipe.io.output.payload.toInt}, 198 | pipe_vo=${dut.pipe.io.output.valid.toBoolean}, 199 | pipe_ro=${dut.pipe.io.output.ready.toBoolean}, 200 | pipe_vi=${dut.pipe.io.input.valid.toBoolean}, 201 | pipe_ri=${dut.pipe.io.input.ready.toBoolean}, 202 | stream_din=${dut.stream.io.sin.payload.toInt}, 203 | stream_dout=${dut.stream.io.sout.payload.toInt}, 204 | stream_vo=${dut.stream.io.sout.valid.toBoolean}, 205 | stream_ro=${dut.stream.io.sout.ready.toBoolean}, 206 | stream_vi=${dut.stream.io.sin.valid.toBoolean}, 207 | stream_ri=${dut.stream.io.sin.ready.toBoolean}, 208 | """) 209 | } 210 | } 211 | 212 | val width = 16 213 | 214 | test("valid pipeline") { 215 | SimConfig 216 | .withFstWave 217 | .compile(new M2SPipeTB(width)) 218 | .doSim(simTest(_)) 219 | } 220 | 221 | test("ready pipeline") { 222 | SimConfig 223 | .withWave 224 | .compile(new S2MPipeTB(width)) 225 | .doSim(simTest(_)) 226 | } 227 | 228 | test("valid pipeline + ready pipeline") { 229 | SimConfig 230 | .withWave 231 | .compile(new BothPipeTB(width)) 232 | .doSim(simTest(_)) 233 | } 234 | 235 | test("ready pipeline + valid pipeline") { 236 | SimConfig 237 | .withWave 238 | .compile(new BothPipe2TB(width)) 239 | .doSim(simTest(_)) 240 | } 241 | 242 | test("pipeline fsm") { 243 | SimConfig 244 | .withWave 245 | .compile(new BothPipe3TB(width)) 246 | .doSim(simTest(_)) 247 | } 248 | } 249 | 250 | -------------------------------------------------------------------------------- /exercises/Examples/test/src/scala/exercises/WeightedArbiterTest.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | import spinal.core._ 6 | import spinal.core.sim._ 7 | import spinal.lib._ 8 | import spinal.sim._ 9 | 10 | class WeightedArbiterTest extends AnyFunSuite { 11 | test("weighted arbiter test") { 12 | val numReq = 8 13 | val weightWidth = 4 14 | val compiled = SimConfig.withWave.allOptimisation.compile( 15 | new WeightedArbiter(numReq, weightWidth) 16 | ) 17 | .doSim { dut => 18 | dut.clockDomain.forkStimulus(5) 19 | 20 | dut.io.clear #= true 21 | val numNonZeroWeights = (0 until numReq).filter(_ > 0).length 22 | for (idx <- 0 until numReq) { 23 | dut.io.weights(idx) #= idx 24 | } 25 | dut.io.reqs #= 0 26 | 27 | dut.clockDomain.waitSampling(3) 28 | dut.io.clear #= false 29 | 30 | val reqVal = scala.math.pow(2, numReq).toInt - 1 31 | dut.clockDomain.waitSampling(3) 32 | dut.io.reqs #= reqVal - 1 33 | 34 | for (itr <- 0 until numReq) { 35 | dut.clockDomain.waitSampling() 36 | dut.io.reqs #= dut.io.reqs.toInt - dut.io.grant.toInt 37 | if (dut.io.reqs.toInt > 0) { 38 | assert( 39 | dut.io.grant.toInt == (1 << (itr + 1)), 40 | s"grant=${dut.io.grant.toInt} not match expected ${1 << (itr + 1)} when itr=$itr" 41 | ) 42 | } 43 | } 44 | 45 | val numRemainingNonZeroWeights = numNonZeroWeights - 1 46 | dut.io.reqs #= reqVal 47 | for { 48 | numNonZeroWeightReqs <- (numRemainingNonZeroWeights until 0 by -1) 49 | bitIdx <- 0 until numNonZeroWeightReqs 50 | } { 51 | dut.clockDomain.waitSampling() 52 | val grantBitIdx = bitIdx + numReq - numNonZeroWeightReqs 53 | // println(s"grant=${dut.io.grant.toInt}, numNonZeroWeightReqs=$numNonZeroWeightReqs, grantBitIdx=$grantBitIdx") 54 | val expectedGrant = 1 << grantBitIdx 55 | assert( 56 | dut.io.grant.toInt == expectedGrant, 57 | s"grant=${dut.io.grant.toInt} not match expected $expectedGrant" 58 | ) 59 | } 60 | 61 | for { 62 | itr <- 0 until 3 63 | numNonZeroWeightReqs <- (numNonZeroWeights until 0 by -1) 64 | bitIdx <- 0 until numNonZeroWeightReqs 65 | } { 66 | dut.clockDomain.waitSampling() 67 | val grantBitIdx = bitIdx + numReq - numNonZeroWeightReqs 68 | // println(s"grant=${dut.io.grant.toInt}, itr=$itr, numNonZeroWeightReqs=$numNonZeroWeightReqs, grantBitIdx=$grantBitIdx") 69 | val expectedGrant = 1 << grantBitIdx 70 | assert( 71 | dut.io.grant.toInt == expectedGrant, 72 | s"grant=${dut.io.grant.toInt} not match expected $expectedGrant" 73 | ) 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /exercises/build.sc: -------------------------------------------------------------------------------- 1 | import $ivy.`com.goyeau::mill-scalafix:0.2.2` 2 | import com.goyeau.mill.scalafix.ScalafixModule 3 | import mill._, scalalib._, scalafmt._ 4 | 5 | val SpinalVersion = "1.6.0" 6 | 7 | trait CommonSpinalModule extends ScalaModule with ScalafmtModule with ScalafixModule { 8 | def scalaVersion = "2.13.6" 9 | def scalacOptions = Seq("-unchecked", "-deprecation", "-feature") 10 | 11 | def ivyDeps = Agg( 12 | ivy"com.github.spinalhdl::spinalhdl-core:$SpinalVersion", 13 | ivy"com.github.spinalhdl::spinalhdl-lib:$SpinalVersion", 14 | ivy"com.github.spinalhdl::spinalhdl-sim:$SpinalVersion", 15 | ) 16 | 17 | def scalacPluginIvyDeps = Agg(ivy"com.github.spinalhdl::spinalhdl-idsl-plugin:$SpinalVersion") 18 | 19 | def scalafixIvyDeps = Agg(ivy"com.github.liancheng::organize-imports:0.5.0") 20 | } 21 | 22 | object Examples extends CommonSpinalModule { 23 | object test extends Tests with TestModule.ScalaTest { 24 | def ivyDeps = Agg(ivy"org.scalatest::scalatest:3.2.9") 25 | def testOnly(args: String*) = T.command { 26 | super.runMain("org.scalatest.run", args: _*) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o xtrace 6 | 7 | MILL_VERSION=0.9.7 8 | 9 | if [ ! -f mill ]; then 10 | curl -L https://github.com/com-lihaoyi/mill/releases/download/$MILL_VERSION/$MILL_VERSION > mill && chmod +x mill 11 | fi 12 | 13 | ./mill version 14 | cd exercises 15 | 16 | # Check format and lint 17 | ../mill Examples.reformat 18 | ../mill Examples.fix --check 19 | 20 | # Run test and simulation 21 | ../mill Examples.runMain exercises.BoothSim 22 | ../mill Examples.test.testOnly exercises.ApbArbiterTest 23 | ../mill Examples.test.testOnly exercises.ApbBridgeTest 24 | ../mill Examples.test.testOnly exercises.FifoCCTest 25 | ../mill Examples.test.testOnly exercises.HandShakePipeTest 26 | # ../mill Examples.test # Not work yet 27 | -------------------------------------------------------------------------------- /rtl/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/rtl/.keep -------------------------------------------------------------------------------- /source/Index.org: -------------------------------------------------------------------------------- 1 | * Spinal-bootcamp 2 | ** 1.1-Spinal-quick-start 3 | 4 | ** 2.1-Spinal-core-Data-type 5 | ** 2.2-Spinal-core-Combinational 6 | ** 2.3-Spinal-core-Sequential 7 | ** 2.4-Spinal-core-FixPoint 8 | ** 2.5-Spinal-core-Width-propogate 9 | ** 2.6-Spinal-core-Component 10 | ** 2.7-Spinal-core-Area 11 | ** 2.8-Spinal-core-Clock-domain 12 | ** 2.9-Spinal-core-Function 13 | 14 | ** 3.1-Spinal-lib-flow-stream 15 | ** 3.2-Spinal-lib-AMBA-bus 16 | ** 3.3-Spinal-lib-Regif 17 | ** 3.4-Spinal-lib-Soc 18 | ** 3.5-Spinal-lib-VexRiscv 19 | 20 | ** 4.1-Spinal-Example-Simple-ones 21 | *** APB3 definition 22 | *** Carry adder 23 | *** Color summing 24 | *** Counters 25 | *** PLL BlackBox and reset controller 26 | *** RGB to gray 27 | *** Sinus rom 28 | ** 4.2-Spinal-Example-Intermediate-ones 29 | ** 4.3-Spinal-Example-Advanced-ones 30 | 31 | ** 5.1-Spinal-Error-checks 32 | *** Assignement overlap 33 | *** Clock crossing violation 34 | *** Combinational loop 35 | *** Hierarchy violation 36 | *** Io bundle 37 | *** Latch detected 38 | *** No driver on 39 | *** NullPointerException 40 | *** Register defined as component input 41 | *** Scope violation 42 | *** Spinal can’t clone class 43 | *** Unassigned register 44 | *** Assignement example 45 | *** Operator example 46 | ** 5.1-Spinal-FAQ 47 | 48 | ** 6.1-env-chisel 49 | ** 6.2-env-breeze 50 | ** 6.2-env-sourcecode 51 | 52 | ** 7.1-Scala-case-pattern 53 | ** 7.2-Scala-implicit 54 | ** 7.3-Scala-reflect 55 | ** 7.4-Scala-Macro 56 | ** 7.5-Scala-utils 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /source/InterruptRegIf2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | InterruptRegIf2 7 | 8 | 109 | 110 | 111 |
112 |

InterruptRegIf2 Interface Document

113 |
114 |
115 | 116 |
117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 |
AddressOffsetRegNameDescriptionWidthSectionFieldNameR/WReset valueField-Description
0x0M_INT_ENABLESInterrupt Enable Reigsiter 32[31:6]--NA0x0Reserved
[5]srch_finish_enRW0x0int enavle register
[4]scd_done_enRW0x0int enavle register
[3]grp_done_enRW0x0int enavle register
[2]ssc_done_enRW0x0int enavle register
[1]pth_done_enRW0x0int enavle register
[0]psc_done_enRW0x0int enavle register
0x4M_INT_MASKInterrupt Mask Reigsiter 32[31:6]--NA0x0Reserved
[5]srch_finish_maskRW0x0int mask register
[4]scd_done_maskRW0x0int mask register
[3]grp_done_maskRW0x0int mask register
[2]ssc_done_maskRW0x0int mask register
[1]pth_done_maskRW0x0int mask register
[0]psc_done_maskRW0x0int mask register
0x8M_INT_STATUSInterrupt status Reigsiter 32[31:6]--NA0x0Reserved
[5]srch_finish_statRC0x0int status register
[4]scd_done_statRC0x0int status register
[3]grp_done_statRC0x0int status register
[2]ssc_done_statRC0x0int status register
[1]pth_done_statRC0x0int status register
[0]psc_done_statRC0x0int status register
274 |
275 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /source/SpinalHDL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/SpinalHDL.png -------------------------------------------------------------------------------- /source/a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/a.gif -------------------------------------------------------------------------------- /source/addertree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/addertree1.png -------------------------------------------------------------------------------- /source/addertree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/addertree2.png -------------------------------------------------------------------------------- /source/b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/b.gif -------------------------------------------------------------------------------- /source/coursier: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/coursier -------------------------------------------------------------------------------- /source/custom.js: -------------------------------------------------------------------------------- 1 | require(['base/js/namespace', 'base/js/events'], 2 | function (Jupyter, events) { 3 | // save a reference to the cell we're currently executing inside of, 4 | // to avoid clearing it later (which would remove this js) 5 | var this_cell = $(element).closest('.cell').data('cell'); 6 | function clear_other_cells () { 7 | Jupyter.notebook.get_cells().forEach(function (cell) { 8 | if (cell.cell_type === 'code' && cell !== this_cell) { 9 | cell.clear_output(); 10 | } 11 | Jupyter.notebook.set_dirty(true); 12 | }); 13 | } 14 | 15 | if (Jupyter.notebook._fully_loaded) { 16 | // notebook has already been fully loaded, so clear now 17 | clear_other_cells(); 18 | } 19 | // Also clear on any future load 20 | // (e.g. when notebook finishes loading, or when a checkpoint is reloaded) 21 | events.on('notebook_loaded.Notebook', clear_other_cells); 22 | }); 23 | -------------------------------------------------------------------------------- /source/jupyter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/jupyter.png -------------------------------------------------------------------------------- /source/load-spinal.sc: -------------------------------------------------------------------------------- 1 | import $ivy.`com.github.spinalhdl::spinalhdl-core:1.7.0` 2 | import $ivy.`com.github.spinalhdl::spinalhdl-lib:1.7.0` 3 | import $plugin.$ivy.`com.github.spinalhdl::spinalhdl-idsl-plugin:1.7.0` 4 | 5 | // import ammonite.ops.Path //http://ammonite.io/ 6 | // interp.load.cp(Path("/Users/mache/.ivy2/local/com.github.spinalhdl/spinalhdl-core_2.11/1.4.1/jars/spinalhdl-core_2.11.jar")) 7 | // interp.load.cp(Path("/Users/mache/.ivy2/local/com.github.spinalhdl/spinalhdl-lib_2.11/1.4.1/jars/spinalhdl-lib_2.11.jar")) 8 | // interp.load.cp(Path("/Users/mache/.ivy2/local/com.github.spinalhdl/spinalhdl-idsl-payload_2.11/1.4.1/jars/spinalhdl-idsl-payload_2.11.jar")) 9 | // interp.load.cp(Path("/Users/mache/.ivy2/local/com.github.spinalhdl/spinalhdl-idsl-plugin_2.11/1.4.1/jars/spinalhdl-idsl-plugin_2.11.jar")) 10 | 11 | // import $ivy.`org.scalanlp::breeze:0.13.2` 12 | 13 | import spinal.core._ 14 | import spinal.lib._ 15 | import spinal.core.sim._ 16 | 17 | implicit class SpinalReportExtend(sp :SpinalReport[Component]) { 18 | def getRtlString_local(): String = { 19 | assert(sp.generatedSourcesPaths.size > 0) 20 | scala.io.Source.fromFile(sp.generatedSourcesPaths.head).mkString 21 | } 22 | } 23 | 24 | val spcfg = SpinalConfig( 25 | defaultConfigForClockDomains = ClockDomainConfig( 26 | clockEdge = RISING, 27 | resetKind = ASYNC, 28 | resetActiveLevel = LOW 29 | ), 30 | headerWithDate = true, 31 | removePruned = false, 32 | anonymSignalPrefix = "t", 33 | mergeAsyncProcess = true, 34 | targetDirectory = "rtl/" 35 | ) 36 | 37 | def showRtl(dut: => Component, mode: SpinalMode = `Verilog`) = { 38 | println(spcfg.copy(mode = mode).generate(dut).getRtlString_local) 39 | } 40 | 41 | def showVhdl(dut: => Component) = showRtl(dut, VHDL) 42 | 43 | @deprecated("Deprecated, showRtl is recommended", "spinal-bootcamp 0.1.0") 44 | def showVerilog(dut: => Component, moduleName: String) = { 45 | spcfg.copy(mode = Verilog).generate(dut) 46 | println(scala.io.Source.fromFile("rtl/"+moduleName+".v").mkString) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /source/regif-gen-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/regif-gen-doc.png -------------------------------------------------------------------------------- /source/regif-gen-int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/regif-gen-int.png -------------------------------------------------------------------------------- /source/sine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijingg/Spinal-bootcamp/6e0454bf09b82fa0d8f72012af72e4d00f2d1921/source/sine.png --------------------------------------------------------------------------------