diff --git a/.github/workflows/template-cleanup.yml b/.github/workflows/template-cleanup.yml new file mode 100644 index 0000000..8c394eb --- /dev/null +++ b/.github/workflows/template-cleanup.yml @@ -0,0 +1,62 @@ +# Workflow to cleanup projects created from this template +# Adapted from workflow of the same name in https://github.com/JetBrains/intellij-platform-plugin-template +# SPDX-License-Identifier: Apache-2.0 + +name: Template Cleanup +on: + push: + branches: [main] + +jobs: + # Triggered on first push to repos created from the template + template-cleanup: + name: Template Cleanup + permissions: + contents: write + runs-on: ubuntu-latest + if: github.event.repository.name != 'chisel-template' + steps: + + - name: Checkout + uses: actions/checkout@v4 + with: + # These are needed to make ad-m/github-push-action work + # https://github.com/ad-m/github-push-action/tree/d91a481090679876dfc4178fef17f286781251df#example-workflow-file + persist-credentials: false + fetch-depth: 0 + + - name: Cleanup + run: | + export LC_CTYPE=C + export LANG=C + + # Prepare variables + NAME="${GITHUB_REPOSITORY##*/}" + ORG="$GITHUB_REPOSITORY_OWNER" + SAFE_ORG=$(echo $ORG | sed 's/[^a-zA-Z0-9]//g' | tr '[:upper:]' '[:lower:]') + GROUP="com.github.$SAFE_ORG" + + # Replace placeholders + sed -i "s/%NAME%/$NAME/g" build.sbt build.sc README.md src/test/scala/gcd/* + sed -i "s/%REPOSITORY%/${GITHUB_REPOSITORY/\//\\/}/g" README.md + sed -i "s/%ORGANIZATION%/$GROUP/g" build.sbt + + # Remove lines marked with #REMOVE-ON-CLEANUP# + sed -i '/#REMOVE-ON-CLEANUP#/d' README.md + + rm -rf \ + .github/workflows/template-cleanup.yml \ + LICENSE + + - name: Commit + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add . + git commit -m "Template cleanup" + + - name: Push changes + uses: ad-m/github-push-action@v0.8.0 + with: + branch: main + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..eb323e7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,59 @@ +name: Continuous Integration + +on: + push: + tags: ['*'] + branches: ['main'] + pull_request: + workflow_dispatch: + +env: + verilator-version: v5.022 + verilator-install-dir: verilator-install + +jobs: + ci: + name: ci + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Cleanup + run: sed -i "s/%NAME%/test/g" build.sc + - name: Cache Scala + uses: coursier/cache-action@v6 + - name: Setup Scala + uses: coursier/setup-action@v1 + with: + jvm: adopt:11 + apps: sbt mill + - name: Setup Dependencies + run: | + sudo apt-get install ccache + - name: Get Cached Verilator + id: get-cached-verilator + uses: actions/cache@v4 + with: + path: ${{ env.verilator-install-dir }} + key: verilator-${{ env.verilator-version }} + - name: Install Verilator + if: steps.get-cached-verilator.outputs.cache-hit != 'true' + run: | + sudo apt-get install git help2man perl python3 make autoconf g++ flex bison numactl perl-doc libfl-dev + git clone https://github.com/verilator/verilator + unset VERILATOR_ROOT + cd verilator + git checkout ${{ env.verilator-version }} + autoconf + ./configure --prefix=$(pwd)/../${{ env.verilator-install-dir }} + make + make install + - name: Set PATH + run: | + echo "$(pwd)/${{ env.verilator-install-dir }}/bin" >> $GITHUB_PATH + echo VERILATOR_ROOT="$(pwd)/${{ env.verilator-install-dir }}/share/verilator" >> $GITHUB_ENV + ln -sf $(pwd)/${{ env.verilator-install-dir }}/bin/verilator_bin $(pwd)/${{ env.verilator-install-dir }}/share/verilator/verilator_bin + - name: SBT Test + run: sbt test + - name: mill Test + run: mill _.test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12033d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,341 @@ +### Project Specific stuff +test_run_dir/* +### XilinxISE template +# intermediate build files +*.bgn +*.bit +*.bld +*.cmd_log +*.drc +*.ll +*.lso +*.msd +*.msk +*.ncd +*.ngc +*.ngd +*.ngr +*.pad +*.par +*.pcf +*.prj +*.ptwx +*.rbb +*.rbd +*.stx +*.syr +*.twr +*.twx +*.unroutes +*.ut +*.xpi +*.xst +*_bitgen.xwbt +*_envsettings.html +*_map.map +*_map.mrp +*_map.ngm +*_map.xrpt +*_ngdbuild.xrpt +*_pad.csv +*_pad.txt +*_par.xrpt +*_summary.html +*_summary.xml +*_usage.xml +*_xst.xrpt + +# project-wide generated files +*.gise +par_usage_statistics.html +usage_statistics_webtalk.html +webtalk.log +webtalk_pn.xml + +# generated folders +iseconfig/ +xlnx_auto_0_xdb/ +xst/ +_ngo/ +_xmsgs/ +### Eclipse template +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Java annotation processor (APT) +.factorypath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse +### C template +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +### SBT template +# Simple Build Tool +# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control + +target/ +lib_managed/ +src_managed/ +project/boot/ +.history +.cache +### Emacs template +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +### Vim template +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +### C++ template +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +### OSX template +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### Xcode template +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +### Scala template +*.class +*.log +/.bsp + +# sbt specific +.cache +.history +.lib/ +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ + +# Scala-IDE specific +.scala_dependencies +.worksheet +### Java template +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + diff --git a/.mill-version b/.mill-version new file mode 100644 index 0000000..62d5dbd --- /dev/null +++ b/.mill-version @@ -0,0 +1 @@ +0.11.5 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..db79f5a --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +Chisel Project Template +======================= + +You've done the [Chisel Bootcamp](https://github.com/freechipsproject/chisel-bootcamp), and now you +are ready to start your own Chisel project. The following procedure should get you started +with a clean running [Chisel3](https://www.chisel-lang.org/) project. + +## Make your own Chisel3 project + +### Dependencies + +#### JDK 8 or newer + +We recommend LTS releases Java 8 and Java 11. You can install the JDK as your operating system recommends, or use the prebuilt binaries from [AdoptOpenJDK](https://adoptopenjdk.net/). + +#### SBT or mill + +SBT is the most common build tool in the Scala community. You can download it [here](https://www.scala-sbt.org/download.html). +mill is another Scala/Java build tool without obscure DSL like SBT. You can download it [here](https://github.com/com-lihaoyi/mill/releases) + +#### Verilator + +The test with `svsim` needs Verilator installed. +See Verilator installation instructions [here](https://verilator.org/guide/latest/install.html). + +### How to get started + +#### Create a repository from the template + +This repository is a Github template. You can create your own repository from it by clicking the green `Use this template` in the top right. +Please leave `Include all branches` **unchecked**; checking it will pollute the history of your new repository. +For more information, see ["Creating a repository from a template"](https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template). + +#### Wait for the template cleanup workflow to complete + +After using the template to create your own blank project, please wait a minute or two for the `Template cleanup` workflow to run which will removes some template-specific stuff from the repository (like the LICENSE). +Refresh the repository page in your browser until you see a 2nd commit by `actions-user` titled `Template cleanup`. + + +#### Clone your repository + +Once you have created a repository from this template and the `Template cleanup` workflow has completed, you can click the green button to get a link for cloning your repository. +Note that it is easiest to push to a repository if you set up SSH with Github, please see the [related documentation](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/connecting-to-github-with-ssh). SSH is required for pushing to a Github repository when using two-factor authentication. + +```sh +git clone git@github.com:%REPOSITORY%.git +cd %NAME% +``` +(The variables wrapped in `%` will be filled in by the template cleanup) + +#### Set project organization and name in build.sbt + +The cleanup workflow will have attempted to provide sensible defaults for `ThisBuild / organization` and `name` in the `build.sbt`. +Feel free to use your text editor of choice to change them as you see fit. + +#### Clean up the README.md file + +Again, use you editor of choice to make the README specific to your project. + +#### Add a LICENSE file + +It is important to have a LICENSE for open source (or closed source) code. +This template repository has the Unlicense in order to allow users to add any license they want to derivative code. +The Unlicense is stripped when creating a repository from this template so that users do not accidentally unlicense their own work. + +For more information about a license, check out the [Github Docs](https://docs.github.com/en/free-pro-team@latest/github/building-a-strong-community/adding-a-license-to-a-repository). + +#### Commit your changes +```sh +git commit -m 'Starting %NAME%' +git push origin main +``` + +### Did it work? + +You should now have a working Chisel3 project. + +You can run the included test with: +```sh +sbt test +``` + +Alternatively, if you use Mill: +```sh +mill %NAME%.test +``` + +You should see a whole bunch of output that ends with something like the following lines +``` +[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 +[info] All tests passed. +[success] Total time: 5 s, completed Dec 16, 2020 12:18:44 PM +``` +If you see the above then... + +### It worked! + +You are ready to go. We have a few recommended practices and things to do. + +* Use packages and following conventions for [structure](https://www.scala-sbt.org/1.x/docs/Directories.html) and [naming](http://docs.scala-lang.org/style/naming-conventions.html) +* Package names should be clearly reflected in the testing hierarchy +* Build tests for all your work +* Read more about testing in SBT in the [SBT docs](https://www.scala-sbt.org/1.x/docs/Testing.html) +* This template includes a [test dependency](https://www.scala-sbt.org/1.x/docs/Library-Dependencies.html#Per-configuration+dependencies) on [ScalaTest](https://www.scalatest.org/). This, coupled with `svsim` (included with Chisel) and `verilator`, are a starting point for testing Chisel generators. + * You can remove this dependency in the build.sbt file if you want to +* Change the name of your project in the build.sbt file +* Change your README.md + +## Problems? Questions? + +Check out the [Chisel Users Community](https://www.chisel-lang.org/community.html) page for links to get in contact! diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..9d4e98d --- /dev/null +++ b/build.sbt @@ -0,0 +1,24 @@ +// See README.md for license details. + +ThisBuild / scalaVersion := "2.13.12" +ThisBuild / version := "0.1.0" +ThisBuild / organization := "%ORGANIZATION%" + +val chiselVersion = "6.2.0" + +lazy val root = (project in file(".")) + .settings( + name := "%NAME%", + libraryDependencies ++= Seq( + "org.chipsalliance" %% "chisel" % chiselVersion, + "org.scalatest" %% "scalatest" % "3.2.16" % "test", + ), + scalacOptions ++= Seq( + "-language:reflectiveCalls", + "-deprecation", + "-feature", + "-Xcheckinit", + "-Ymacro-annotations", + ), + addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full), + ) diff --git a/build.sc b/build.sc new file mode 100644 index 0000000..30badf0 --- /dev/null +++ b/build.sc @@ -0,0 +1,30 @@ +// import Mill dependency +import mill._ +import mill.define.Sources +import mill.modules.Util +import mill.scalalib.TestModule.ScalaTest +import scalalib._ +// support BSP +import mill.bsp._ + +object %NAME% extends SbtModule { m => + override def millSourcePath = os.pwd + override def scalaVersion = "2.13.12" + override def scalacOptions = Seq( + "-language:reflectiveCalls", + "-deprecation", + "-feature", + "-Xcheckinit", + ) + override def ivyDeps = Agg( + ivy"org.chipsalliance::chisel:6.2.0", + ) + override def scalacPluginIvyDeps = Agg( + ivy"org.chipsalliance:::chisel-plugin:6.2.0", + ) + object test extends SbtModuleTests with TestModule.ScalaTest { + override def ivyDeps = m.ivyDeps() ++ Agg( + ivy"org.scalatest::scalatest::3.2.16" + ) + } +} diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..b19d4e1 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version = 1.9.7 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..5708f81 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1 @@ +logLevel := Level.Warn diff --git a/src/main/scala/gcd/DecoupledGCD.scala b/src/main/scala/gcd/DecoupledGCD.scala new file mode 100644 index 0000000..1cd2993 --- /dev/null +++ b/src/main/scala/gcd/DecoupledGCD.scala @@ -0,0 +1,73 @@ +// See README.md for license details. + +package gcd + +import chisel3._ +import chisel3.util.Decoupled + +class GcdInputBundle(val w: Int) extends Bundle { + val value1 = UInt(w.W) + val value2 = UInt(w.W) +} + +class GcdOutputBundle(val w: Int) extends Bundle { + val value1 = UInt(w.W) + val value2 = UInt(w.W) + val gcd = UInt(w.W) +} + +/** + * Compute Gcd using subtraction method. + * Subtracts the smaller from the larger until register y is zero. + * value input register x is then the Gcd. + * Unless first input is zero then the Gcd is y. + * Can handle stalls on the producer or consumer side + */ +class DecoupledGcd(width: Int) extends Module { + val input = IO(Flipped(Decoupled(new GcdInputBundle(width)))) + val output = IO(Decoupled(new GcdOutputBundle(width))) + + val xInitial = Reg(UInt()) + val yInitial = Reg(UInt()) + val x = Reg(UInt()) + val y = Reg(UInt()) + val busy = RegInit(false.B) + val resultValid = RegInit(false.B) + + input.ready := ! busy + output.valid := resultValid + output.bits := DontCare + + when(busy) { + when(x > y) { + x := x - y + }.otherwise { + y := y - x + } + when(x === 0.U || y === 0.U) { + when(x === 0.U) { + output.bits.gcd := y + }.otherwise { + output.bits.gcd := x + } + + output.bits.value1 := xInitial + output.bits.value2 := yInitial + resultValid := true.B + + when(output.ready && resultValid) { + busy := false.B + resultValid := false.B + } + } + }.otherwise { + when(input.valid) { + val bundle = input.deq() + x := bundle.value1 + y := bundle.value2 + xInitial := bundle.value1 + yInitial := bundle.value2 + busy := true.B + } + } +} diff --git a/src/main/scala/gcd/GCD.scala b/src/main/scala/gcd/GCD.scala new file mode 100644 index 0000000..42c5e80 --- /dev/null +++ b/src/main/scala/gcd/GCD.scala @@ -0,0 +1,46 @@ +// See README.md for license details. + +package gcd + +import chisel3._ +// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._ +import _root_.circt.stage.ChiselStage + +/** + * Compute GCD using subtraction method. + * Subtracts the smaller from the larger until register y is zero. + * value in register x is then the GCD + */ +class GCD extends Module { + val io = IO(new Bundle { + val value1 = Input(UInt(16.W)) + val value2 = Input(UInt(16.W)) + val loadingValues = Input(Bool()) + val outputGCD = Output(UInt(16.W)) + val outputValid = Output(Bool()) + }) + + val x = Reg(UInt()) + val y = Reg(UInt()) + + when(x > y) { x := x - y } + .otherwise { y := y - x } + + when(io.loadingValues) { + x := io.value1 + y := io.value2 + } + + io.outputGCD := x + io.outputValid := y === 0.U +} + +/** + * Generate Verilog sources and save it in file GCD.v + */ +object GCD extends App { + ChiselStage.emitSystemVerilogFile( + new GCD, + firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info") + ) +} diff --git a/src/test/scala/gcd/GCDSpec.scala b/src/test/scala/gcd/GCDSpec.scala new file mode 100644 index 0000000..e8c7ce8 --- /dev/null +++ b/src/test/scala/gcd/GCDSpec.scala @@ -0,0 +1,68 @@ +// See README.md for license details. + +package gcd + +import chisel3._ +import chisel3.experimental.BundleLiterals._ +import chisel3.simulator.EphemeralSimulator._ +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers + +/** + * This is a trivial example of how to run this Specification + * From within sbt use: + * {{{ + * testOnly gcd.GCDSpec + * }}} + * From a terminal shell use: + * {{{ + * sbt 'testOnly gcd.GCDSpec' + * }}} + * Testing from mill: + * {{{ + * mill %NAME%.test.testOnly gcd.GCDSpec + * }}} + */ +class GCDSpec extends AnyFreeSpec with Matchers { + + "Gcd should calculate proper greatest common denominator" in { + simulate(new DecoupledGcd(16)) { dut => + val testValues = for { x <- 0 to 10; y <- 0 to 10} yield (x, y) + val inputSeq = testValues.map { case (x, y) => (new GcdInputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U) } + val resultSeq = testValues.map { case (x, y) => + (new GcdOutputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U, _.gcd -> BigInt(x).gcd(BigInt(y)).U) + } + + dut.reset.poke(true.B) + dut.clock.step() + dut.reset.poke(false.B) + dut.clock.step() + + var sent, received, cycles: Int = 0 + while (sent != 100 && received != 100) { + assert(cycles <= 1000, "timeout reached") + + if (sent < 100) { + dut.input.valid.poke(true.B) + dut.input.bits.value1.poke(testValues(sent)._1.U) + dut.input.bits.value2.poke(testValues(sent)._2.U) + if (dut.input.ready.peek().litToBoolean) { + sent += 1 + } + } + + if (received < 100) { + dut.output.ready.poke(true.B) + if (dut.output.valid.peekValue().asBigInt == 1) { + dut.output.bits.gcd.expect(BigInt(testValues(received)._1).gcd(testValues(received)._2)) + received += 1 + } + } + + // Step the simulation forward. + dut.clock.step() + cycles += 1 + } + } + } +}