├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── build.sbt ├── setup.sh ├── src └── main │ └── scala │ └── bottlerocket │ ├── AXI4Lite.scala │ ├── BottleRocketCore.scala │ ├── BottleRocketGenerator.scala │ ├── DebugModuleFSM.scala │ ├── DebugStepper.scala │ ├── ExceptionCause.scala │ ├── FrontendBuffer.scala │ ├── LoadExtender.scala │ ├── Params.scala │ ├── RVFI.scala │ └── RegfileWithZero.scala └── test ├── BottleRocketCoreTB.v ├── Makefile ├── MockAXI4LiteSRAM.v └── test.ld /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary files 2 | *~ 3 | *# 4 | 5 | # File extensions 6 | *.swp 7 | *.objdump 8 | *.o 9 | *.svwf 10 | *.dump 11 | *.log 12 | *.elf 13 | *.hex 14 | *.trn 15 | *.dsn 16 | test/*.d 17 | test/*.ld 18 | test/*.addrs 19 | 20 | # Directories 21 | /generated-src/ 22 | /path-check-output/ 23 | /lib/ 24 | /target/ 25 | /project/target/ 26 | /test/target/ 27 | *.simvision/ 28 | */INCA_libs/ 29 | */ncverilog.history 30 | 31 | # Specific files 32 | .addons-dont-touch 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/rocket-chip"] 2 | path = third_party/rocket-chip 3 | url = https://github.com/freechipsproject/rocket-chip 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | GENDIR := generated-src 16 | 17 | RVEC ?= 4100 18 | 19 | default: $(GENDIR)/BottleRocketCore.v 20 | 21 | .PHONY: $(GENDIR)/BottleRocketCore.v 22 | $(GENDIR)/BottleRocketCore.v: 23 | rm -rf generated-src/* 24 | sbt "runMain bottlerocket.BottleRocketGenerator --target-dir generated-src --reset-vec ${RVEC}" 25 | 26 | .PHONY: check-paths test clean 27 | 28 | check-paths: 29 | sbt "runMain bottlerocket.BottleRocketPathChecker --target-dir path-check-output --reset-vec ${RVEC}" 30 | 31 | test: $(GENDIR)/BottleRocketCore.v 32 | $(MAKE) -C test clean 33 | $(MAKE) -C test 34 | 35 | clean: 36 | $(MAKE) -C test clean 37 | rm -rf generated-src 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BottleRocket RV32IMC Core 2 | 3 | This is not an officially supported Google product. 4 | 5 | ## Overview 6 | 7 | BottleRocket is a 32-bit, RISC-V microcontroller-class processor core that is 8 | built as a customized microarchitecture from components of the Free Chips 9 | Project Rocket core. It is implemented in the Chisel HDL, and it consists of a 10 | basic, 3-stage pipeline with separate instruction and data ARM AMBA AXI4Lite 11 | buses. It has an assortment of features that are designed to support typical use 12 | as a control processor for memory-mapped devices. 13 | 14 | ## Features 15 | 16 | * RV32IMC ISA, Privileged Architecture v1.10 17 | * 32-bit RISC-V base instruction set (‘RV32I’) 18 | * Hardware integer multiplier/divider (‘M’ standard extension) 19 | * 16-bit compressed instruction support (‘C’ standard extension) 20 | * Machine (‘M’) and user (‘U’) privilege modes 21 | 22 | ## Design Rationale 23 | 24 | The BottleRocket core is designed to be as simple as possible to allow for easy, 25 | application-specific changes. It uses several key components from Rocket Chip, 26 | an open-source RISC-V chip generator framework, including the instruction 27 | decoder and control & status register (CSR) finite state machine. These two 28 | components are responsible for implementing the majority of the nuanced features 29 | of the user ISA and the privileged architecture, respectively. This approach has 30 | several key advantages. 31 | 32 | * Rocket Chip is the reference implementation of the RISC-V ISA. It is largely 33 | produced by the primary authors of the ISA specifications, and it is by far 34 | the most spec-compliant hardware implementation. 35 | 36 | * However, Rocket Chip is quite complex. It has many performance-oriented 37 | microarchitectural features (integrated non-blocking data cache, branch 38 | prediction) 39 | 40 | * Rocket Chip is a very heavily metaprogrammed framework for generating 41 | symmetric multiprocessor (SMP) systems with interconnect supporting multiple 42 | coherent caches. In order to use the core in a simpler context, creating a 43 | simpler top-level module would be desirable for readability purposes. 44 | 45 | * The “Rocket” core that is used in Rocket Chip is largely composed of 46 | well-factored, reasonably large, and highly-reusable sub-blocks. These blocks 47 | have been used in multiple projects to create different core microarchitectures 48 | or pipelines with relatively low effort (BOOM, ZScale) 49 | 50 | * The instruction decoder is implemented as a reusable combinational block that 51 | is essentially universally applicable to any RISC-V core where decode happens 52 | within a single stage. It is well-verified and supports all of the RISC-V 53 | standard extensions in their latest incarnations. 54 | 55 | * The RISC-V compressed (RVC) expander is also implemented as a reusable 56 | combinational block. Because every RVC instruction maps onto a normal 32-bit 57 | encoding, this universal expander handles all of the RVC extension, aside from 58 | the extra complication of designing fetch logic to handle 16-bit aligned 59 | program counters. 60 | 61 | * The CSR file implements essentially all of the privileged architecture as a 62 | state machine. 63 | 64 | * Building around Rocket components allows BottleRocket to be a fully 65 | spec-compliant RV32IMC core with machine and user modes while occupying a few 66 | hundred lines of total new Chisel code. 67 | 68 | ## Building and Running 69 | 70 | The first step to using BottleRocket is making sure that the work environment is 71 | ready to support RISC-V development. It is helpful to follow the convention that 72 | the RISCV environment variable points to the RISC-V toolchain installation. 73 | 74 | 1. Add the following to your environment using configuration files and/or a 75 | script 76 | 77 | ```bash 78 | $ export RISCV= 79 | $ export PATH=$PATH:$RISCV/bin 80 | ``` 81 | 82 | 2. Clone and install the RV32IMC toolchain. Note, this requires changing the 83 | meta-build script that calls configure and make in each process, as shown 84 | with the sed invocation below. 85 | 86 | ```bash 87 | $ git clone https://github.com/riscv/riscv-tools 88 | $ cd riscv-tools 89 | $ sed 's/ima/imc/g' build-rv32imc.sh 90 | $ chmod +x build-rv32imc.sh 91 | $ ./build-rv32imc.sh 92 | ``` 93 | 94 | 3. Enter the BottleRocket directory and run the standalone tests. NOTE: you may 95 | need to modify `test/Makefile` to target an appropriate Verilog simulator for 96 | your environment. 97 | 98 | ```bash 99 | $ cd 100 | $ make test 101 | ``` 102 | 103 | 4. The generated Verilog is in `generated-src/BottleRocketCore.v` -- this 104 | contains the top level BottleRocketCore module that can be instantiated in a 105 | Verilog design. 106 | 107 | 108 | 5. Try running sbt (“Simple Build Tool,” the most popular build tool for Scala 109 | projects) manually. This allows more options for building the core. The 110 | following command will print all available command-line options (number of 111 | interrupt lines, target directory for Verilog generation, etc.). 112 | 113 | ```bash 114 | $ sbt "runMain bottlerocket.BottleRocketGenerator --help" 115 | ``` 116 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) 16 | 17 | // managed dependencies 18 | val chisel3 = "edu.berkeley.cs" %% "chisel3" % "3.0-SNAPSHOT_2017-06-22" 19 | val scalatest = "org.scalatest" %% "scalatest" % "2.2.5" 20 | val scalacheck = "org.scalacheck" %% "scalacheck" % "1.12.4" 21 | 22 | lazy val commonSettings = Seq( 23 | name := "bottlerocket", 24 | version := "1.0", 25 | scalaVersion := "2.11.7", 26 | resolvers ++= Seq( 27 | Resolver.sonatypeRepo("snapshots"), 28 | Resolver.sonatypeRepo("releases") 29 | ), 30 | libraryDependencies ++= Seq(chisel3, scalatest, scalacheck) 31 | ) 32 | 33 | lazy val hardfloat = (project in file("./third_party/rocket-chip/hardfloat")) 34 | .settings( 35 | commonSettings, 36 | name := "hardfloat" 37 | ) 38 | 39 | 40 | lazy val rocketchip = (project in file("./third_party/rocket-chip")) 41 | .dependsOn(hardfloat) 42 | .settings( 43 | commonSettings, 44 | name := "rocket-chip", 45 | addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) 46 | ) 47 | 48 | lazy val root = (project in file(".")) 49 | .dependsOn(rocketchip) 50 | .settings( 51 | commonSettings, 52 | name := "bottlerocket" 53 | ) 54 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Setup script to parsimoniously clone submodules 18 | 19 | git submodule update --init third_party/rocket-chip 20 | cd third_party/rocket-chip 21 | git submodule update --init --recursive hardfloat 22 | git submodule update --init riscv-tools 23 | cd riscv-tools 24 | git submodule update --init --recursive riscv-tests 25 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/AXI4Lite.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Structural definition of AXI4Lite channel bundles 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.Irrevocable 21 | import freechips.rocketchip 22 | import rocketchip.util.GenericParameterizedBundle 23 | import rocketchip.amba.axi4.AXI4BundleParameters 24 | 25 | abstract class AXI4LiteBundleBase(params: AXI4BundleParameters) extends GenericParameterizedBundle(params) 26 | 27 | abstract class AXI4LiteBundleA(params: AXI4BundleParameters) extends AXI4LiteBundleBase(params) 28 | { 29 | val addr = UInt(width = params.addrBits) 30 | val cache = UInt(width = params.cacheBits) 31 | val prot = UInt(width = params.protBits) 32 | } 33 | 34 | class AXI4LiteBundleAW(params: AXI4BundleParameters) extends AXI4LiteBundleA(params) 35 | class AXI4LiteBundleAR(params: AXI4BundleParameters) extends AXI4LiteBundleA(params) 36 | 37 | class AXI4LiteBundleW(params: AXI4BundleParameters) extends AXI4LiteBundleBase(params) 38 | { 39 | val data = UInt(width = params.dataBits) 40 | val strb = UInt(width = params.dataBits/8) 41 | } 42 | 43 | class AXI4LiteBundleR(params: AXI4BundleParameters) extends AXI4LiteBundleBase(params) 44 | { 45 | val data = UInt(width = params.dataBits) 46 | val resp = UInt(width = params.respBits) 47 | } 48 | 49 | class AXI4LiteBundleB(params: AXI4BundleParameters) extends AXI4LiteBundleBase(params) 50 | { 51 | val resp = UInt(width = params.respBits) 52 | } 53 | 54 | class AXI4LiteBundle(params: AXI4BundleParameters) extends AXI4LiteBundleBase(params) 55 | { 56 | val aw = Irrevocable(new AXI4LiteBundleAW(params)) 57 | val w = Irrevocable(new AXI4LiteBundleW (params)) 58 | val b = Flipped(Irrevocable(new AXI4LiteBundleB (params))) 59 | val ar = Irrevocable(new AXI4LiteBundleAR(params)) 60 | val r = Flipped(Irrevocable(new AXI4LiteBundleR (params))) 61 | } 62 | 63 | object AXI4LiteBundle 64 | { 65 | def apply(params: AXI4BundleParameters) = new AXI4LiteBundle(params) 66 | } 67 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/BottleRocketCore.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Top-level pipeline for the BottleRocket core 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.{RegEnable} 21 | import chisel3.core.withClock 22 | import freechips.rocketchip._ 23 | import rocket._ 24 | import devices.debug.DMIIO 25 | import amba.axi4.{AXI4Bundle, AXI4Parameters} 26 | import Params._ 27 | 28 | class BottleRocketCore(options: BROptions)(implicit p: config.Parameters) extends Module { 29 | val io = IO(new Bundle { 30 | val constclk = Input(Clock()) 31 | val iBus = AXI4LiteBundle(axiParams) 32 | val dBus = AXI4LiteBundle(axiParams) 33 | val nmi = Input(Bool()) 34 | val eip = Input(Bool()) 35 | val dmi = Flipped(new DMIIO()(p)) 36 | val wfisleep = Output(Bool()) 37 | val traceInst = Output(UInt(width = xBitwidth)) 38 | val traceRetire = Output(Bool()) 39 | val traceInterrupt = Output(Bool()) 40 | val traceEret = Output(Bool()) 41 | val rvfi = new RVFI 42 | }) 43 | 44 | /******************** 45 | * MAJOR SUBMODULES * 46 | ********************/ 47 | 48 | val fetchMgr = Module(new FrontendBuffer(options)) 49 | val regfile = new RegfileWithZero(nRegisters, xBitwidth) 50 | val alu = Module(new ALU()) 51 | val debug = Module(new DebugModuleFSM()) 52 | val csrfile = Module(new CSRFile()) 53 | val breakpoints = Module(new BreakpointUnit(csrfile.nBreakpoints)) 54 | 55 | /*************** 56 | * WFISLEEP FF * 57 | ***************/ 58 | 59 | // No need to wait for outstanding IBus request; manager is designed for this case 60 | val s_ready :: s_sleeping :: s_waking :: Nil = chisel3.util.Enum(UInt(), 3) 61 | val sleepstate = withClock(io.constclk) { Reg(init = s_ready) } 62 | 63 | fetchMgr.io.sleeping := sleepstate =/= s_ready 64 | io.wfisleep := sleepstate === s_sleeping && !fetchMgr.io.outstanding 65 | 66 | when (sleepstate === s_ready) { 67 | when (csrfile.io.csr_stall) { 68 | sleepstate := s_sleeping 69 | } 70 | } .elsewhen (sleepstate === s_sleeping) { 71 | when (io.eip) { 72 | sleepstate := s_waking 73 | } 74 | } .otherwise { 75 | when (!csrfile.io.csr_stall) { 76 | sleepstate := s_ready 77 | } 78 | } 79 | 80 | /****************************** 81 | * IMPRECISE STORE INTERRUPTS * 82 | ******************************/ 83 | 84 | val impreciseStoreInterrupt = Reg(init = Bool(false)) 85 | 86 | /******************************* 87 | * HAZARD AND REDIRECT SIGNALS * 88 | *******************************/ 89 | 90 | // Hazard and redirect signals 91 | val wait_IF = Wire(Bool()) 92 | val stall_IF = Wire(Bool()) 93 | val kill_IF = Wire(Bool()) 94 | val stall_EX = Wire(Bool()) 95 | val kill_EX = Wire(Bool()) 96 | val stall_WB = Wire(Bool()) 97 | val kill_WB = Wire(Bool()) 98 | val exception_WB = Wire(Bool()) 99 | val nextPC = Wire(UInt(width = xBitwidth)) 100 | 101 | val busReqWait_EX = Wire(Bool()) 102 | val mulDivReqWait_EX = Wire(Bool()) 103 | val busRespWait_WB = Wire(Bool()) 104 | val mulDivRespWait_WB = Wire(Bool()) 105 | 106 | // Memory kill signals 107 | val memStructuralHazard = Wire(Bool()) 108 | val killMem_EX = Wire(Bool()) 109 | 110 | /********************** 111 | * PIPELINE REGISTERS * 112 | **********************/ 113 | 114 | val canonicalNOP = UInt(0x13, width = instBitwidth) 115 | 116 | val resetHold = Reg(init = Bool(true)) 117 | 118 | val PC_IF = Wire(UInt(width = xBitwidth)) 119 | 120 | val PC_EX = Reg(init = UInt(0, width = xBitwidth)) 121 | val instPreExp_EX = Reg(init = canonicalNOP) 122 | val pastExceptions_EX = Reg(init = ExceptionCause.clear) 123 | val isBubble_EX = Reg(init = Bool(true)) 124 | 125 | val isBubble_WB = Reg(init = Bool(true)) 126 | val PC_WB = Reg(init = UInt(0, width = xBitwidth)) 127 | val rvc_WB = Reg(init = Bool(false)) 128 | val pastExceptions_WB = Reg(init = ExceptionCause.clear) 129 | val jal_WB = Reg(init = Bool(false)) 130 | val jalr_WB = Reg(init = Bool(false)) 131 | val memEn_WB = Reg(init = Bool(false)) 132 | val memAddrOffset_WB = Reg(init = UInt(0, width = xByteOffsetBitwidth)) 133 | val isStore_WB = Reg(init = Bool(false)) 134 | val memType_WB = Reg(init = UInt(0, width = MT_SZ)) 135 | val writeReg_WB = Reg(init = Bool(false)) 136 | val rdAddr_WB = Reg(init = UInt(0)) 137 | val csrCmd_WB = Reg(init = CSR.N) 138 | val mulDiv_WB = Reg(init = Bool(false)) 139 | val aluOut_WB = Reg(init = UInt(0)) 140 | val inst_WB = Reg(init = canonicalNOP) 141 | 142 | /*************************** 143 | * INSTRUCTION FETCH STAGE * 144 | ***************************/ 145 | 146 | PC_IF := fetchMgr.io.resp.bits.pc 147 | val instPreExp_IF = fetchMgr.io.resp.bits.inst 148 | val rvc_IF = instPreExp_IF(1,0) =/= UInt(3) 149 | val exceptions_IF = Wire(new ExceptionCause) 150 | exceptions_IF := ExceptionCause.clear 151 | 152 | fetchMgr.io.req.enter_U_mode := csrfile.io.eret && !csrfile.io.status.debug && 153 | csrfile.io.status.mpp === UInt(PRV.U) 154 | fetchMgr.io.req.exit_U_mode := csrfile.io.exception && !(csrfile.io.cause === UInt(CSR.debugTriggerCause)) 155 | fetchMgr.io.req.pc := nextPC 156 | fetchMgr.io.resp.ready := !stall_IF 157 | io.iBus <> fetchMgr.io.bus 158 | 159 | val busWerr = io.dBus.b.valid && (io.dBus.b.bits.resp === AXI4Parameters.RESP_SLVERR || io.dBus.b.bits.resp === AXI4Parameters.RESP_DECERR) 160 | val werrExceptionTaken = exception_WB && csrfile.io.cause === UInt(Causes.store_access) 161 | when (busWerr) { 162 | impreciseStoreInterrupt := Bool(true) 163 | } .elsewhen(werrExceptionTaken) { 164 | impreciseStoreInterrupt := Bool(false) 165 | } 166 | 167 | exceptions_IF.interrupt := csrfile.io.interrupt 168 | exceptions_IF.storeFault := impreciseStoreInterrupt 169 | exceptions_IF.misalignedFetch := fetchMgr.io.resp.valid && PC_IF(0) 170 | exceptions_IF.illegalFetch := fetchMgr.io.resp.valid && fetchMgr.io.resp.bits.error 171 | 172 | /************************ 173 | * DECODE/EXECUTE STAGE * 174 | ************************/ 175 | 176 | when (!stall_EX) { 177 | PC_EX := PC_IF 178 | instPreExp_EX := instPreExp_IF 179 | pastExceptions_EX := exceptions_IF 180 | isBubble_EX := kill_IF 181 | } 182 | 183 | val rvc_EX = instPreExp_EX(1,0) =/= UInt(3) 184 | val instExp_EX = new RVCDecoder(instPreExp_EX).decode 185 | val inst_EX = instExp_EX.bits 186 | 187 | val decoder_data = Seq(new MDecode, new IDecode).flatMap(_.table) 188 | val ctrl_EX = Wire(new IntCtrlSigs()).decode(inst_EX, decoder_data) 189 | val memEn_EX = ctrl_EX.mem && (ctrl_EX.mem_cmd === M_XWR || ctrl_EX.mem_cmd === M_XRD) 190 | val isLoad_EX = ctrl_EX.mem && ctrl_EX.mem_cmd === M_XRD 191 | val isStore_EX = ctrl_EX.mem && ctrl_EX.mem_cmd === M_XWR 192 | 193 | val rdAddr_EX = instExp_EX.rd 194 | val rs1Addr_EX = instExp_EX.rs1 195 | val rs2Addr_EX = instExp_EX.rs2 196 | val shamt_EX = inst_EX(24, 20) 197 | val imm_EX = ImmGen(ctrl_EX.sel_imm, inst_EX) 198 | 199 | val csrAccess_EX = (ctrl_EX.csr === CSR.S) || (ctrl_EX.csr === CSR.C) || (ctrl_EX.csr === CSR.W) 200 | val csrRead_EX = rs1Addr_EX === UInt(0) && (ctrl_EX.csr =/= CSR.N) 201 | val csrCmd_EX = Mux(csrRead_EX, CSR.R, ctrl_EX.csr) 202 | 203 | val rs1AddrDebugMuxed_EX = Mux(csrfile.io.status.debug, debug.io.gpraddr, rs1Addr_EX) 204 | val rs1Data_EX = regfile.read(rs1AddrDebugMuxed_EX) 205 | val rs2Data_EX = regfile.read(rs2Addr_EX) 206 | 207 | val rdReady_WB = !(memEn_WB || mulDiv_WB) && (csrCmd_WB === CSR.N) 208 | val writing_WB = !isBubble_WB && writeReg_WB && rdAddr_WB =/= UInt(0) 209 | val rs1RAW_EX = writing_WB && !isBubble_EX && ctrl_EX.rxs1 && rs1Addr_EX === rdAddr_WB 210 | val rs2RAW_EX = writing_WB && !isBubble_EX && ctrl_EX.rxs2 && rs2Addr_EX === rdAddr_WB 211 | val rs1BypassALUResult_EX = rs1RAW_EX && rdReady_WB 212 | val rs2BypassALUResult_EX = rs2RAW_EX && rdReady_WB 213 | val unbypassable_EX = (rs1RAW_EX || rs2RAW_EX) && !rdReady_WB 214 | val rs1DataBypassed_EX = Mux(rs1BypassALUResult_EX,aluOut_WB,rs1Data_EX) 215 | val rs2DataBypassed_EX = Mux(rs2BypassALUResult_EX,aluOut_WB,rs2Data_EX) 216 | 217 | val arg1_EX = Wire(SInt(width = xBitwidth)) 218 | when (ctrl_EX.sel_alu1 === A1_RS1) { 219 | arg1_EX := rs1DataBypassed_EX.asSInt 220 | } .elsewhen (ctrl_EX.sel_alu1 === A1_PC) { 221 | arg1_EX := PC_EX.asSInt 222 | } .otherwise { 223 | arg1_EX := SInt(0) 224 | } 225 | 226 | val arg2_EX = Wire(SInt(width = xBitwidth)) 227 | when (ctrl_EX.sel_alu2 === A2_RS2) { 228 | arg2_EX := rs2DataBypassed_EX.asSInt 229 | } .elsewhen (ctrl_EX.sel_alu2 === A2_IMM) { 230 | arg2_EX := imm_EX 231 | } .elsewhen (ctrl_EX.sel_alu2 === A2_SIZE) { 232 | arg2_EX := Mux(rvc_EX,SInt(2),SInt(4)) 233 | } .otherwise { 234 | arg2_EX := SInt(0) 235 | } 236 | 237 | alu.io.fn := ctrl_EX.alu_fn 238 | alu.io.dw := DW_32 239 | alu.io.in1 := arg1_EX.asUInt 240 | alu.io.in2 := arg2_EX.asUInt 241 | 242 | val memSize_EX = Wire(UInt()) 243 | val wdata_EX = Wire(UInt(width = xBitwidth)) 244 | when (ctrl_EX.mem_type === MT_B || ctrl_EX.mem_type === MT_BU) { 245 | memSize_EX := UInt(1) 246 | wdata_EX := chisel3.util.Cat(Seq.fill(4){ rs2DataBypassed_EX(7,0) }) 247 | } .elsewhen (ctrl_EX.mem_type === MT_H || ctrl_EX.mem_type === MT_HU) { 248 | memSize_EX := UInt(2) 249 | wdata_EX := chisel3.util.Cat(Seq.fill(2){ rs2DataBypassed_EX(15,0) }) 250 | } .otherwise { 251 | memSize_EX := UInt(4) 252 | wdata_EX := rs2DataBypassed_EX 253 | } 254 | 255 | csrfile.io.decode.csr := inst_EX(31,20) 256 | val illegalExtension = ctrl_EX.amo || ctrl_EX.fp || ctrl_EX.dp || ctrl_EX.rocc 257 | val illegalCSRAccess = csrfile.io.decode.read_illegal && csrAccess_EX 258 | val illegalCSRWrite = csrfile.io.decode.write_illegal && csrAccess_EX && !csrRead_EX 259 | val illegalSystem = csrfile.io.decode.system_illegal && ((ctrl_EX.mem && ctrl_EX.mem_cmd === M_SFENCE) || (ctrl_EX.csr >= CSR.I)) 260 | val illegalInst_EX = !ctrl_EX.legal || illegalExtension || illegalCSRAccess || illegalCSRWrite || illegalSystem 261 | val misaligned_EX = (alu.io.adder_out & (memSize_EX - UInt(1))).orR 262 | val pcBreakpoint = breakpoints.io.xcpt_if 263 | val debugBreakpoint = breakpoints.io.debug_if 264 | val loadBreakpoint = isLoad_EX && breakpoints.io.xcpt_ld 265 | val storeBreakpoint = isStore_EX && breakpoints.io.xcpt_st 266 | val loadDebugBreakpoint = isLoad_EX && breakpoints.io.debug_ld 267 | val storeDebugBreakpoint = isStore_EX && breakpoints.io.debug_st 268 | 269 | val exceptions_EX = Wire(new ExceptionCause) 270 | exceptions_EX := pastExceptions_EX 271 | exceptions_EX.illegalInstruction := illegalInst_EX 272 | exceptions_EX.breakpoint := pcBreakpoint || loadBreakpoint || storeBreakpoint 273 | exceptions_EX.debugBreakpoint := debugBreakpoint || loadDebugBreakpoint || storeDebugBreakpoint 274 | exceptions_EX.loadMisaligned := misaligned_EX && isLoad_EX 275 | exceptions_EX.storeMisaligned := misaligned_EX && isStore_EX 276 | 277 | // Do not access memory with an unhandled exception! 278 | val memEnMasked_EX = memEn_EX && !killMem_EX && !ExceptionCause.toBool(exceptions_EX) 279 | val awSentWUnsent_EX = RegInit(Bool(false)) 280 | when (io.dBus.aw.fire && !io.dBus.w.fire) { 281 | awSentWUnsent_EX := Bool(true) 282 | } .elsewhen (io.dBus.w.fire && !io.dBus.aw.fire) { 283 | awSentWUnsent_EX := Bool(false) 284 | } 285 | 286 | io.dBus.ar.valid := memEnMasked_EX && isLoad_EX 287 | io.dBus.ar.bits.addr := alu.io.adder_out.asUInt 288 | io.dBus.ar.bits.cache := AXI4Parameters.CACHE_BUFFERABLE 289 | io.dBus.ar.bits.prot := Mux(csrfile.io.status.prv === UInt(PRV.M), AXI4Parameters.PROT_PRIVILEDGED, AXI4Parameters.PROT_INSECURE) 290 | 291 | io.dBus.aw.valid := memEnMasked_EX && isStore_EX && !awSentWUnsent_EX 292 | io.dBus.aw.bits.addr := alu.io.adder_out.asUInt 293 | io.dBus.aw.bits.cache := AXI4Parameters.CACHE_BUFFERABLE 294 | io.dBus.aw.bits.prot := Mux(csrfile.io.status.prv === UInt(PRV.M), AXI4Parameters.PROT_PRIVILEDGED, AXI4Parameters.PROT_INSECURE) 295 | 296 | io.dBus.w.valid := memEnMasked_EX && isStore_EX 297 | io.dBus.w.bits.data := wdata_EX 298 | io.dBus.w.bits.strb := ((1.U << memSize_EX) - 1.U) << io.dBus.aw.bits.addr(1,0) 299 | 300 | // Multiplier/Divider 301 | val mulDivEnMasked_EX = !kill_EX && ctrl_EX.div 302 | val mulDiv = Module(new MulDiv(cfg = MulDivParams(), width = xBitwidth.get)) 303 | mulDiv.io.req.valid := mulDivEnMasked_EX 304 | mulDiv.io.req.bits.dw := ctrl_EX.alu_dw 305 | mulDiv.io.req.bits.fn := ctrl_EX.alu_fn 306 | mulDiv.io.req.bits.in1 := rs1DataBypassed_EX 307 | mulDiv.io.req.bits.in2 := rs2DataBypassed_EX 308 | mulDiv.io.req.bits.tag := rdAddr_EX 309 | 310 | breakpoints.io.pc := PC_EX 311 | breakpoints.io.ea := alu.io.adder_out 312 | 313 | /******************* 314 | * WRITEBACK STAGE * 315 | *******************/ 316 | 317 | when (!stall_WB) { 318 | isBubble_WB := kill_EX || busReqWait_EX 319 | PC_WB := PC_EX 320 | rvc_WB := rvc_EX 321 | pastExceptions_WB := exceptions_EX 322 | jal_WB := ctrl_EX.jal 323 | jalr_WB := ctrl_EX.jalr 324 | writeReg_WB := ctrl_EX.wxd 325 | rdAddr_WB := rdAddr_EX 326 | memEn_WB := memEnMasked_EX 327 | memAddrOffset_WB := alu.io.adder_out 328 | isStore_WB := isStore_EX 329 | memType_WB := ctrl_EX.mem_type 330 | csrCmd_WB := ctrl_EX.csr 331 | mulDiv_WB := mulDivEnMasked_EX 332 | aluOut_WB := alu.io.out 333 | inst_WB := inst_EX 334 | } 335 | 336 | val writeRegMasked_WB = !kill_WB && writeReg_WB 337 | 338 | // No exceptions *generated in WB stage* should alter cmd, as they are system-related 339 | val hasPastExceptions = ExceptionCause.toBool(pastExceptions_WB) 340 | val csrCmdMasked_WB = Mux(isBubble_WB || hasPastExceptions, CSR.N, csrCmd_WB) 341 | 342 | val exceptions_WB = Wire(new ExceptionCause) 343 | exceptions_WB := pastExceptions_WB 344 | 345 | val busRerr = io.dBus.b.valid && (io.dBus.b.bits.resp === AXI4Parameters.RESP_SLVERR || io.dBus.b.bits.resp === AXI4Parameters.RESP_DECERR) 346 | val pendingLoadError = memEn_WB && busRerr 347 | 348 | // Load errors are delayed a cycle by stalling the writeback stage to avoid bus-to-bus paths 349 | val delayedLoadError = RegNext(next = pendingLoadError, init = Bool(false)) 350 | exceptions_WB.loadFault := delayedLoadError 351 | 352 | // Always ready for bus replies 353 | io.dBus.r.ready := Bool(true) 354 | io.dBus.b.ready := Bool(true) 355 | 356 | // Always ready for mulDiv replies 357 | mulDiv.io.resp.ready := Bool(true) 358 | 359 | // The only time accumulated causes get ignored is with a bubble in the WB stage 360 | exception_WB := ExceptionCause.toBool(exceptions_WB) && !isBubble_WB 361 | 362 | // Only supports a single hart 363 | csrfile.io.hartid := UInt(0) 364 | 365 | // Overwrite CSR cmd/addr/wdata in debug mode 366 | when (csrfile.io.status.debug) { 367 | when (debug.io.debugRet) { 368 | csrfile.io.rw.cmd := CSR.I 369 | csrfile.io.rw.addr := "h7b2".U // dret addr 370 | } .otherwise { 371 | csrfile.io.rw.cmd := Mux(debug.io.csrwrite, CSR.W, CSR.R) 372 | csrfile.io.rw.addr := debug.io.csraddr 373 | } 374 | csrfile.io.rw.wdata := debug.io.regwdata 375 | } .otherwise { 376 | csrfile.io.rw.addr := inst_WB(31, 20) 377 | csrfile.io.rw.cmd := csrCmdMasked_WB 378 | csrfile.io.rw.wdata := aluOut_WB 379 | } 380 | 381 | val busErrCause = csrfile.io.cause === UInt(Causes.store_access) || csrfile.io.cause === UInt(Causes.load_access) 382 | csrfile.io.exception := exception_WB || io.nmi 383 | csrfile.io.retire := !isBubble_WB && !kill_WB 384 | csrfile.io.cause := ExceptionCause.toCause(exceptions_WB, csrfile.io.interrupt_cause) 385 | csrfile.io.pc := PC_WB 386 | csrfile.io.badaddr := aluOut_WB // TODO (amagyar): AXI4 store error address 387 | csrfile.io.fcsr_flags.valid := Bool(false) 388 | csrfile.io.interrupts.meip := io.eip 389 | csrfile.io.interrupts.mtip := Bool(false) // TODO (amagyar): support timer interrupt 390 | csrfile.io.interrupts.msip := Bool(false) // TODO (amagyar): support software interrupt 391 | csrfile.io.interrupts.lip := UInt(0).asTypeOf(csrfile.io.interrupts.lip) 392 | csrfile.io.rocc_interrupt := UInt(0) 393 | 394 | breakpoints.io.status := csrfile.io.status 395 | breakpoints.io.bp := csrfile.io.bp 396 | 397 | val loadExtender = Module(new LoadExtender) 398 | loadExtender.io.offset := memAddrOffset_WB 399 | loadExtender.io.in := io.dBus.r.bits.data 400 | loadExtender.io.memType := memType_WB 401 | 402 | val regWriteData_WB = Wire(UInt(width = xBitwidth)) 403 | when (memEn_WB) { 404 | regWriteData_WB := loadExtender.io.out 405 | } .elsewhen (mulDiv_WB) { 406 | regWriteData_WB := mulDiv.io.resp.bits.data 407 | } .elsewhen (csrCmd_WB =/= CSR.N) { 408 | regWriteData_WB := csrfile.io.rw.rdata 409 | } .elsewhen (jalr_WB) { 410 | regWriteData_WB := PC_WB + Mux(rvc_WB,UInt(2),UInt(4)) 411 | } .otherwise { 412 | regWriteData_WB := aluOut_WB 413 | } 414 | 415 | /******************** 416 | * DEBUG GPR ACCESS * 417 | ********************/ 418 | 419 | val regfileWriteData = Mux(csrfile.io.status.debug, debug.io.regwdata, regWriteData_WB) 420 | val regfileWriteAddr = Mux(csrfile.io.status.debug, debug.io.gpraddr, rdAddr_WB) 421 | 422 | when (writeRegMasked_WB || debug.io.gprwrite) { 423 | regfile.write(regfileWriteAddr, regfileWriteData) 424 | } 425 | 426 | /*********** 427 | * TRACING * 428 | * *********/ 429 | 430 | io.traceEret := csrfile.io.eret 431 | io.traceInterrupt := exception_WB 432 | io.traceRetire := csrfile.io.retire 433 | io.traceInst := inst_WB 434 | 435 | /***************************** 436 | * HAZARD AND REDIRECT LOGIC * 437 | *****************************/ 438 | 439 | val readReqWait_EX = io.dBus.ar.valid && !io.dBus.ar.ready 440 | val writeReqWait_EX = (io.dBus.aw.valid && !io.dBus.aw.ready) || (io.dBus.w.valid && !io.dBus.w.ready) 441 | busReqWait_EX := readReqWait_EX || writeReqWait_EX 442 | mulDivReqWait_EX := !isBubble_EX && mulDiv.io.req.valid && !mulDiv.io.req.ready 443 | busRespWait_WB := !isBubble_WB && memEn_WB && ((!isStore_WB && !io.dBus.r.valid) || (isStore_WB && !io.dBus.b.valid)) 444 | mulDivRespWait_WB := !isBubble_WB && mulDiv_WB && !mulDiv.io.resp.valid 445 | 446 | wait_IF := Bool(false) 447 | stall_IF := Bool(false) 448 | kill_IF := Bool(false) 449 | 450 | stall_EX := Bool(false) 451 | kill_EX := Bool(false) 452 | 453 | stall_WB := Bool(false) 454 | kill_WB := Bool(false) 455 | 456 | when (isBubble_EX) { 457 | kill_EX := Bool(true) 458 | } 459 | 460 | when (isBubble_WB) { 461 | kill_WB := Bool(true) 462 | } 463 | 464 | val incPC_EX = PC_EX + Mux(rvc_EX, UInt(2), UInt(4)) 465 | val targetPC_EX = Wire(UInt(width = xBitwidth)) 466 | val redirect_EX = !isBubble_EX && (ctrl_EX.jal || ctrl_EX.jalr || (ctrl_EX.branch && alu.io.cmp_out)) // can eliminate cmp check for constant time 467 | targetPC_EX := incPC_EX 468 | when (ctrl_EX.jalr) { 469 | targetPC_EX := alu.io.out ^ alu.io.out(0) 470 | } .elsewhen(ctrl_EX.jal ||(ctrl_EX.branch && alu.io.cmp_out)) { 471 | targetPC_EX := PC_EX + imm_EX.asUInt 472 | } 473 | 474 | val incAmt_IF = Mux(rvc_IF, UInt(2), UInt(4)) 475 | when (io.nmi) { 476 | nextPC := UInt(p(NMI_VEC)) 477 | } .elsewhen (exception_WB || csrfile.io.eret) { 478 | nextPC := csrfile.io.evec 479 | } .otherwise { 480 | nextPC := targetPC_EX 481 | } 482 | 483 | fetchMgr.io.req.redirect := exception_WB || io.nmi || csrfile.io.eret || (redirect_EX && !unbypassable_EX) 484 | memStructuralHazard := memEn_EX && !isBubble_EX && memEn_WB && !isBubble_WB 485 | killMem_EX := isBubble_EX || memStructuralHazard || exception_WB || io.nmi || csrfile.io.eret || mulDivRespWait_WB || csrfile.io.csr_stall || unbypassable_EX 486 | 487 | when (csrfile.io.status.debug) { 488 | kill_IF := Bool(true) 489 | kill_EX := Bool(true) 490 | kill_WB := Bool(true) 491 | stall_IF := Bool(true) 492 | } .elsewhen (pendingLoadError) { 493 | stall_IF := Bool(true) 494 | kill_IF := Bool(true) 495 | stall_EX := Bool(true) 496 | kill_EX := Bool(true) 497 | stall_WB := Bool(true) 498 | kill_WB := Bool(true) 499 | } .elsewhen (exception_WB || io.nmi) { 500 | kill_IF := Bool(true) 501 | kill_EX := Bool(true) 502 | kill_WB := Bool(true) 503 | } .elsewhen (csrfile.io.eret) { 504 | kill_IF := Bool(true) 505 | kill_EX := Bool(true) 506 | } .elsewhen (busRespWait_WB || mulDivRespWait_WB || csrfile.io.csr_stall) { 507 | stall_IF := Bool(true) 508 | kill_IF := Bool(true) 509 | stall_EX := Bool(true) 510 | kill_EX := Bool(true) 511 | stall_WB := Bool(true) 512 | kill_WB := Bool(true) 513 | } .elsewhen (memStructuralHazard || unbypassable_EX) { 514 | stall_IF := Bool(true) 515 | kill_IF := Bool(true) 516 | stall_EX := Bool(true) 517 | kill_EX := Bool(true) 518 | } .elsewhen (busReqWait_EX || mulDivReqWait_EX) { 519 | stall_IF := Bool(true) 520 | kill_IF := Bool(true) 521 | stall_EX := Bool(true) 522 | } .elsewhen (redirect_EX) { 523 | kill_IF := Bool(true) 524 | } .elsewhen (!fetchMgr.io.resp.valid) { 525 | kill_IF := Bool(true) 526 | wait_IF := Bool(true) 527 | } 528 | 529 | /********* 530 | * DEBUG * 531 | *********/ 532 | 533 | val singleStepped = Reg(init = Bool(false)) 534 | val issuing_IF = !(kill_IF || wait_IF) 535 | val issued = !(isBubble_EX && isBubble_WB) 536 | when (csrfile.io.singleStep && (issuing_IF || issued)) { 537 | singleStepped := Bool(true) 538 | } .elsewhen (csrfile.io.status.debug) { 539 | singleStepped := Bool(false) 540 | } 541 | 542 | debug.io.singleStepHalt := csrfile.io.status.debug && singleStepped 543 | debug.io.debugModeActive := csrfile.io.status.debug 544 | debug.io.csrrdata := csrfile.io.rw.rdata 545 | debug.io.gprrdata := rs1Data_EX 546 | csrfile.io.interrupts.debug := debug.io.debugInt || singleStepped 547 | when (singleStepped) { 548 | exceptions_IF.interrupt := Bool(true) 549 | } 550 | 551 | debug.io.dmi <> io.dmi 552 | 553 | /******** 554 | * RVFI * 555 | ********/ 556 | 557 | // pipeline some otherwise unnecessary signals to WB stage 558 | val rvfi_rs1Addr_WB = RegEnable(rs1Addr_EX, !stall_WB) 559 | val rvfi_rs1Data_WB = RegEnable(rs1DataBypassed_EX, !stall_WB) 560 | val rvfi_rs2Addr_WB = RegEnable(rs2Addr_EX, !stall_WB) 561 | val rvfi_rs2Data_WB = RegEnable(rs2DataBypassed_EX, !stall_WB) 562 | val rvfi_nextPC_WB_from_EX = RegEnable(targetPC_EX, !stall_WB) 563 | val rvfi_store_data_WB = RegEnable(wdata_EX, !stall_WB) 564 | val rvfi_retire = csrfile.io.retire === UInt(1) 565 | val rvfi_order = Reg(init = UInt(0, 64.W)) 566 | when (rvfi_retire) { 567 | rvfi_order := rvfi_order + UInt(1) 568 | } 569 | 570 | val rvfi_next_starts_handler = Reg(init = Bool(false)) 571 | when (exception_WB) { 572 | rvfi_next_starts_handler := Bool(true) 573 | } .elsewhen (rvfi_retire) { 574 | rvfi_next_starts_handler := Bool(false) 575 | } 576 | 577 | val rvfi_mem_size_mask = Wire(UInt()) 578 | when (memEn_WB) { 579 | when (memType_WB === MT_B || memType_WB === MT_BU) { 580 | rvfi_mem_size_mask := UInt(1) 581 | } .elsewhen (memType_WB === MT_H || memType_WB === MT_HU) { 582 | rvfi_mem_size_mask := UInt(3) 583 | } .otherwise { 584 | rvfi_mem_size_mask := UInt(15) 585 | } 586 | } .otherwise { 587 | rvfi_mem_size_mask := UInt(0) 588 | } 589 | 590 | io.rvfi.valid := rvfi_retire 591 | io.rvfi.order := rvfi_order 592 | io.rvfi.insn := inst_WB 593 | io.rvfi.trap := exception_WB && (exceptions_WB.illegalInstruction || exceptions_WB.loadMisaligned || exceptions_WB.storeMisaligned) 594 | io.rvfi.halt := Bool(false) // TODO (amagyar): clarify halt meaning 595 | io.rvfi.intr := rvfi_next_starts_handler 596 | io.rvfi.rs1_addr := rvfi_rs1Addr_WB 597 | io.rvfi.rs1_rdata := rvfi_rs1Data_WB 598 | io.rvfi.rs2_addr := rvfi_rs2Addr_WB 599 | io.rvfi.rs2_rdata := rvfi_rs2Data_WB 600 | io.rvfi.rd_addr := Mux((writeRegMasked_WB || debug.io.gprwrite), regfileWriteAddr, UInt(0)) 601 | io.rvfi.rd_wdata := Mux((writeRegMasked_WB || debug.io.gprwrite), regfileWriteData, UInt(0)) 602 | io.rvfi.pc_rdata := PC_WB 603 | io.rvfi.pc_wdata := Mux(exception_WB || csrfile.io.eret || io.nmi, nextPC, rvfi_nextPC_WB_from_EX) 604 | io.rvfi.mem_addr := aluOut_WB 605 | io.rvfi.mem_rmask := Mux(memEn_WB && !isStore_WB, rvfi_mem_size_mask, UInt(0)) 606 | io.rvfi.mem_rdata := Mux(memEn_WB && !isStore_WB, regfileWriteData, UInt(0)) 607 | io.rvfi.mem_wmask := Mux(memEn_WB && isStore_WB, rvfi_mem_size_mask, UInt(0)) 608 | io.rvfi.mem_wdata := Mux(memEn_WB && isStore_WB, rvfi_store_data_WB, UInt(0)) 609 | 610 | } 611 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/BottleRocketGenerator.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Generator for emitting Verilog 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import firrtl.{ExecutionOptionsManager, HasFirrtlOptions, CommonOptions, FirrtlExecutionOptions, ComposableOptions} 21 | 22 | case class BROptions(nProgInterrupts: Int = 240, resetVec: BigInt = BigInt("100", 16)) extends ComposableOptions 23 | 24 | object BottleRocketGenerator extends App { 25 | val config = new DefaultBottleRocketConfig 26 | 27 | trait HasBROptions { 28 | self: ExecutionOptionsManager => 29 | var brOptions = BROptions() 30 | parser.note("BottleRocket options") 31 | parser.opt[Int]("nProgInterrupts") 32 | .abbr("nInts") 33 | .valueName("") 34 | .foreach { n => brOptions = brOptions.copy(nProgInterrupts = n) } 35 | parser.opt[String]("reset-vec") 36 | .abbr("rstVec") 37 | .valueName("") 38 | .foreach { str => brOptions = brOptions.copy(resetVec = BigInt(str, 16)) } 39 | } 40 | 41 | val optionsManager = new ExecutionOptionsManager("chisel3") 42 | with HasChiselExecutionOptions 43 | with HasFirrtlOptions 44 | with HasBROptions { } 45 | 46 | if (optionsManager.parse(args)) { 47 | Driver.execute(optionsManager, () => new BottleRocketCore(optionsManager.brOptions)(config)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/DebugModuleFSM.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Debug Module that controls top-level behaviors of SiFive debug spec v0.13 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.{Enum, Cat, Queue} 21 | import freechips.rocketchip._ 22 | import devices.debug.{DMIReq, DMIResp, DMIIO, DMIConsts, DebugModuleParams} 23 | import Params._ 24 | 25 | class DebugModuleFSM()(implicit p: config.Parameters) extends Module { 26 | val io = IO(new Bundle { 27 | val singleStepHalt = Input(Bool()) 28 | val debugInt = Output(Bool()) 29 | val debugRet = Output(Bool()) 30 | val debugModeActive = Input(Bool()) 31 | val gpraddr = Output(UInt(width = 5.W)) 32 | val csraddr = Output(UInt(width = 12.W)) 33 | val gprwrite = Output(Bool()) 34 | val csrwrite = Output(Bool()) 35 | val gprrdata = Input(UInt(width = xBitwidth)) 36 | val csrrdata = Input(UInt(width = xBitwidth)) 37 | val regwdata = Output(UInt(width = xBitwidth)) 38 | val dmi = Flipped(new DMIIO()(p)) 39 | }) 40 | 41 | val reqQueue = Module(new Queue(new DMIReq(p(DebugModuleParams).nDMIAddrSize), 2)) 42 | val respQueue = Module(new Queue(new DMIResp(), 2)) 43 | 44 | reqQueue.io.enq <> io.dmi.req 45 | reqQueue.io.deq.ready := respQueue.io.enq.ready 46 | 47 | val dmiReqData = reqQueue.io.deq.bits.data 48 | val dmiReqAddr = reqQueue.io.deq.bits.addr 49 | val dmiReadActive = reqQueue.io.deq.fire && reqQueue.io.deq.bits.op === DMIConsts.dmi_OP_READ 50 | val dmiWriteActive = reqQueue.io.deq.fire && reqQueue.io.deq.bits.op === DMIConsts.dmi_OP_WRITE 51 | val dmiRespValid = Reg(init = Bool(false)) 52 | val dmiRespType = Reg(init = DMIConsts.dmi_RESP_SUCCESS) 53 | val dmiRespData = Reg(UInt()) 54 | 55 | dmiRespValid := reqQueue.io.deq.fire 56 | respQueue.io.enq.valid := dmiRespValid 57 | respQueue.io.enq.bits.resp := dmiRespType 58 | respQueue.io.enq.bits.data := dmiRespData 59 | io.dmi.resp <> respQueue.io.deq 60 | 61 | 62 | def zPad(w: Int) = UInt(0, width = w.W) 63 | 64 | val s_running :: s_halting :: s_triggering :: s_halted :: s_resuming :: Nil = chisel3.util.Enum(UInt(), 5) 65 | val haltState = Reg(init = s_running) 66 | 67 | val s_idle :: s_busy :: s_error :: Nil = chisel3.util.Enum(UInt(), 3) 68 | val cmdState = Reg(init = s_idle) 69 | 70 | // abstract data registers 71 | val data0 = Reg(init = zPad(32)) 72 | 73 | // dmcontrol fields 74 | val haltReq = Reg(init = Bool(false)) 75 | val resumeReq = Reg(init = Bool(false)) 76 | val dmActive = Bool(true) 77 | 78 | // dmstatus fields 79 | val running = (haltState === s_running) 80 | val halted = (haltState === s_halted) 81 | val resumeAck = Reg(init = Bool(false)) 82 | val version = UInt(2, width = 4.W) 83 | 84 | // abstractcs fields 85 | val progsize = zPad(5) 86 | val cmdBusy = (cmdState === s_busy) 87 | val cmdErr = Reg(init = zPad(3)) 88 | val datacount = UInt(1, width = 5.W) 89 | 90 | // abstract cmd 91 | val abstractCmd = Reg(init = zPad(32)) 92 | 93 | // read mapping 94 | dmiRespType := DMIConsts.dmi_RESP_SUCCESS 95 | when (dmiReqAddr === "h04".U) { 96 | dmiRespData := data0 97 | } .elsewhen (dmiReqAddr === "h10".U) { 98 | dmiRespData := Cat(haltReq, resumeReq, zPad(29), dmActive) 99 | } .elsewhen (dmiReqAddr === "h11".U) { 100 | dmiRespData := Cat(zPad(14), resumeAck, resumeAck, zPad(4), running, running, halted, halted, zPad(4), version) 101 | } .elsewhen (dmiReqAddr === "h16".U) { 102 | dmiRespData := Cat(zPad(3), progsize, zPad(11), cmdBusy, zPad(1), cmdErr, zPad(3), datacount) 103 | } .elsewhen (dmiReqAddr === "h17".U) { 104 | dmiRespData := abstractCmd 105 | } .otherwise { 106 | dmiRespType := DMIConsts.dmi_RESP_FAILURE 107 | dmiRespData := zPad(32) 108 | } 109 | 110 | val cmdType = abstractCmd(31,24) 111 | val newCmd = dmiWriteActive && (dmiReqAddr === "h17".U) 112 | val writeInCmd = (cmdState === s_busy) && dmiWriteActive 113 | val clearErr = dmiWriteActive && (dmiReqAddr === "h16".U) && dmiReqData(10,8).orR 114 | val invalidCmd = abstractCmd(31,24) =/= UInt(0) 115 | val isGPR = abstractCmd(15,5) === UInt(0x1000, width = 16)(15,5) 116 | val isCSR = abstractCmd(15,12) === UInt(0) 117 | val doRegAccess = (haltState === s_halted) && (cmdState === s_busy) && (cmdType === UInt(0)) && abstractCmd(17) 118 | val doRegWrite = doRegAccess && abstractCmd(16) 119 | val doRegRead = doRegAccess && !abstractCmd(16) 120 | 121 | // write mapping (except cmderr) 122 | when (dmiWriteActive) { 123 | when (dmiReqAddr === "h04".U) { 124 | data0 := dmiReqData 125 | } .elsewhen (dmiReqAddr === "h10".U) { 126 | haltReq := dmiReqData(31) 127 | resumeReq := dmiReqData(30) 128 | resumeAck := Bool(false) 129 | } .elsewhen (dmiReqAddr === "h17".U) { 130 | abstractCmd := dmiReqData 131 | } 132 | } .elsewhen (doRegRead) { 133 | data0 := Mux(isGPR, io.gprrdata, io.csrrdata) 134 | } 135 | 136 | val nextCmdState = Wire(UInt()) 137 | nextCmdState := cmdState 138 | when (cmdState === s_idle) { 139 | when (newCmd) { 140 | nextCmdState := s_busy 141 | } 142 | } .elsewhen (cmdState === s_busy) { 143 | when (writeInCmd || invalidCmd || !halted) { 144 | nextCmdState := s_error 145 | } .otherwise { 146 | nextCmdState := s_idle 147 | } 148 | } .otherwise { 149 | nextCmdState := Mux(clearErr, s_idle, s_error) 150 | } 151 | cmdState := nextCmdState 152 | 153 | when (nextCmdState === s_error) { 154 | when (!writeInCmd) { 155 | cmdErr := UInt(1) 156 | } .elsewhen (!halted) { 157 | cmdErr := UInt(4) 158 | } .otherwise { 159 | cmdErr := UInt(2) 160 | } 161 | } 162 | 163 | // This signal ensures that each resume request causes only ONE resume 164 | val freshResumeReq = resumeReq && !resumeAck 165 | 166 | val nextHaltState = Wire(UInt()) 167 | nextHaltState := haltState 168 | when (haltState === s_running) { 169 | when (io.singleStepHalt) { 170 | nextHaltState := s_halted 171 | } .elsewhen (haltReq) { 172 | nextHaltState := s_halting 173 | } .elsewhen (io.debugModeActive) { 174 | nextHaltState := s_halted // triggering 175 | } 176 | } .elsewhen (haltState === s_halting) { 177 | when (io.debugModeActive) { 178 | nextHaltState := s_halted 179 | } 180 | } .elsewhen (haltState === s_halted) { 181 | when (freshResumeReq) { 182 | nextHaltState := s_resuming 183 | } 184 | } .otherwise { 185 | when (!io.debugModeActive) { 186 | nextHaltState := s_running 187 | resumeAck := Bool(true) 188 | } 189 | } 190 | haltState := nextHaltState 191 | 192 | io.debugInt := haltState === s_halting 193 | io.debugRet := haltState === s_halted && nextHaltState === s_resuming 194 | io.gpraddr := abstractCmd(4,0) 195 | io.csraddr := abstractCmd(11,0) 196 | io.gprwrite := isGPR && doRegWrite 197 | io.csrwrite := isCSR && doRegWrite 198 | io.regwdata := data0 199 | } 200 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/DebugStepper.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Debug stimulus generator that single steps the core continuously 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.{Enum,Cat} 21 | import freechips.rocketchip._ 22 | import devices.debug.{DMIReq, DMIResp, DMIIO, DMIConsts} 23 | import Params._ 24 | 25 | class DebugStepper()(implicit p: config.Parameters) extends Module { 26 | val io = IO(new Bundle { 27 | val dmi = new DMIIO()(p) 28 | }) 29 | 30 | // running: wait for halted to be set 31 | // halted: set resumereq 32 | // resuming: wait for resumeack 33 | 34 | val s_waitForHalt :: s_restart :: s_waitForResume :: Nil = chisel3.util.Enum(UInt(), 3) 35 | val haltState = Reg(init = s_waitForHalt) 36 | 37 | val nextHaltState = Wire(UInt()) 38 | nextHaltState := haltState 39 | 40 | io.dmi.req.valid := Bool(true) 41 | io.dmi.resp.ready := Bool(true) 42 | io.dmi.req.bits.addr := Mux(haltState === s_restart, "h10".U, "h11".U) 43 | io.dmi.req.bits.data := "h40000001".U 44 | io.dmi.req.bits.op := Mux(haltState === s_restart, DMIConsts.dmi_OP_WRITE, DMIConsts.dmi_OP_READ) 45 | 46 | when (haltState === s_waitForHalt) { 47 | when (io.dmi.resp.fire && io.dmi.resp.bits.data(8)) { 48 | haltState := s_restart 49 | } 50 | } .elsewhen (haltState === s_restart) { 51 | when (io.dmi.req.fire) { 52 | haltState := s_waitForResume 53 | } 54 | } .otherwise { 55 | when (io.dmi.resp.fire && io.dmi.resp.bits.data(16)) { 56 | haltState := s_waitForHalt 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/ExceptionCause.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Exception / interrupt cause encodings 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.MuxCase 21 | import freechips.rocketchip.rocket.{CSR, Causes} 22 | 23 | class ExceptionCause extends Bundle { 24 | val misalignedFetch = Bool() 25 | val illegalFetch = Bool() 26 | val illegalInstruction = Bool() 27 | val breakpoint = Bool() 28 | val debugBreakpoint = Bool() 29 | val loadMisaligned = Bool() 30 | val loadFault = Bool() 31 | val storeMisaligned = Bool() 32 | val storeFault = Bool() 33 | val interrupt = Bool() 34 | } 35 | 36 | object ExceptionCause { 37 | def clear: ExceptionCause = 0.U.asTypeOf(new ExceptionCause) 38 | 39 | def toBool(e: ExceptionCause): Bool = e.asUInt.orR 40 | 41 | def toCause(eCause: ExceptionCause, iCause: UInt): UInt = { 42 | MuxCase(iCause, Seq( 43 | (eCause.misalignedFetch, UInt(Causes.misaligned_fetch)), 44 | (eCause.illegalFetch, UInt(Causes.fetch_access)), 45 | (eCause.illegalInstruction, UInt(Causes.illegal_instruction)), 46 | (eCause.breakpoint, UInt(Causes.breakpoint)), 47 | (eCause.debugBreakpoint, UInt(CSR.debugTriggerCause)), 48 | (eCause.loadMisaligned, UInt(Causes.misaligned_load)), 49 | (eCause.loadFault, UInt(Causes.load_access)), 50 | (eCause.storeMisaligned, UInt(Causes.misaligned_store)), 51 | (eCause.storeFault, UInt(Causes.store_access)))) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/FrontendBuffer.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Decoupled frontend implementation with efficient misaligned 32-bit fetch 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.core.withClock 21 | import chisel3.util.{Decoupled,Valid,Cat} 22 | import freechips.rocketchip.amba.axi4.{AXI4Parameters} 23 | import Params._ 24 | 25 | class FrontendReq extends Bundle { 26 | val pc = Input(UInt(xBitwidth)) 27 | val redirect = Input(Bool()) 28 | val enter_U_mode = Input(Bool()) 29 | val exit_U_mode = Input(Bool()) 30 | } 31 | 32 | class FrontendResp extends Bundle { 33 | val pc = Output(UInt(xBitwidth)) 34 | val inst = Output(UInt(instBitwidth)) 35 | val error = Output(Bool()) 36 | } 37 | 38 | class FrontendBuffer(options: BROptions) extends Module { 39 | 40 | val io = IO(new Bundle{ 41 | val outstanding = Output(Bool()) 42 | val sleeping = Input(Bool()) 43 | val req = new FrontendReq 44 | val resp = Decoupled(new FrontendResp) 45 | val bus = AXI4LiteBundle(axiParams) 46 | }) 47 | 48 | def wordAddress(x: UInt) = x & ~UInt(3, width = xBitwidth) 49 | def isRVC(i: UInt) = i(1,0) =/= UInt(3) 50 | def isWordAligned(a: UInt) = a(1,0) === UInt(0) 51 | def min(a: UInt, b: UInt) = Mux(a < b, a, b) 52 | 53 | // Hold reqvalid low during sleep 54 | val reqvalid_ungated = Wire(Bool()) 55 | io.bus.ar.valid := reqvalid_ungated && !io.sleeping 56 | 57 | // These two registers track issued bus requests -> must be gated with bus clock, if different 58 | val n_pending = Reg(UInt(3.W), init = UInt(0)) 59 | val last_req_addr = Reg(UInt(xBitwidth)) 60 | io.outstanding := n_pending =/= UInt(0) 61 | 62 | val n_to_drop = Reg(UInt(3.W), init = UInt(0)) 63 | val true_pending = n_pending - n_to_drop 64 | 65 | val buf_vec = Reg(Vec(4, UInt(16.W))) 66 | val err_vec = Reg(Vec(4, Bool())) 67 | val buf_base = Reg(UInt(xBitwidth), init = UInt(options.resetVec)) 68 | val buf_size = Reg(UInt(4.W), init = UInt(0)) // SIZE IS IN BYTES 69 | val buf_head = Reg(UInt(2.W), init = UInt(0)) // PTRS ARE HALFWORD INDICES 70 | val buf_tail = Reg(UInt(2.W), init = UInt(0)) // PTRS ARE HALFWORD INDICES 71 | val buffer_full = (buf_size + (true_pending << 2)) > UInt(4) || n_pending === UInt(7) 72 | 73 | val clear_buffer = Wire(Bool()) 74 | val drop_outstanding = Wire(Bool()) 75 | val expected_bus_fetch_valid = io.bus.r.valid && n_to_drop === UInt(0) 76 | 77 | val head_halfword = buf_vec(buf_head) 78 | val next_halfword = buf_vec(buf_head + UInt(1)) 79 | val jumped_to_halfword_aligned = Reg(init = Bool(false)) 80 | val n_useful_bus_bytes = Mux(jumped_to_halfword_aligned, UInt(2), UInt(4)) 81 | val bus_first_halfword = Mux(jumped_to_halfword_aligned, io.bus.r.bits.data(31,16), io.bus.r.bits.data(15,0)) 82 | val bus_second_halfword = Mux(jumped_to_halfword_aligned, io.bus.r.bits.data(15,0), io.bus.r.bits.data(31,16)) 83 | 84 | val hold_reset = Reg(init = Bool(true)) 85 | hold_reset := Bool(false) 86 | 87 | // constant AXI4Lite fields 88 | io.bus.ar.bits.cache := AXI4Parameters.CACHE_BUFFERABLE 89 | io.bus.r.ready := Bool(true) 90 | 91 | // zero write channel signals 92 | io.bus.aw.valid := false.B 93 | io.bus.aw.bits.addr := 0.U 94 | io.bus.aw.bits.cache := 0.U 95 | io.bus.aw.bits.prot := 0.U 96 | io.bus.w.valid := false.B 97 | io.bus.w.bits.data := 0.U 98 | io.bus.w.bits.strb := 0.U 99 | io.bus.b.ready := true.B 100 | 101 | // Sometimes redirects go to halfword-aligned addresses 102 | when (io.req.redirect) { 103 | jumped_to_halfword_aligned := !isWordAligned(io.req.pc) 104 | } .elsewhen (expected_bus_fetch_valid) { 105 | jumped_to_halfword_aligned := Bool(false) 106 | } 107 | 108 | // Record last requested address 109 | when (io.bus.ar.ready && io.bus.ar.valid) { 110 | last_req_addr := io.bus.ar.bits.addr 111 | } 112 | 113 | // Privilege modes 114 | val bus_prot = Reg(init = AXI4Parameters.PROT_PRIVILEDGED) 115 | when (io.req.enter_U_mode) { 116 | bus_prot := AXI4Parameters.PROT_INSTRUCTION 117 | } .elsewhen (io.req.exit_U_mode) { 118 | bus_prot := AXI4Parameters.PROT_PRIVILEDGED 119 | } 120 | io.bus.ar.bits.prot := bus_prot 121 | 122 | // two main behaviors: branch/jump/exception/etc or sequential code 123 | // ALL privilege level changes are also redirects, so this handles flushing 124 | when (hold_reset) { 125 | reqvalid_ungated := Bool(false) 126 | io.bus.ar.bits.addr := wordAddress(io.req.pc) 127 | clear_buffer := Bool(false) 128 | drop_outstanding := Bool(false) 129 | } .elsewhen (io.req.redirect) { 130 | reqvalid_ungated := Bool(true) 131 | io.bus.ar.bits.addr := wordAddress(io.req.pc) 132 | clear_buffer := Bool(true) 133 | drop_outstanding := Bool(true) 134 | } .otherwise { 135 | reqvalid_ungated := !buffer_full 136 | io.bus.ar.bits.addr := Mux(true_pending > UInt(0), last_req_addr + UInt(4), wordAddress(buf_base + buf_size)) 137 | clear_buffer := Bool(false) 138 | drop_outstanding := Bool(false) 139 | } 140 | 141 | // outstanding / to-drop transaction counters 142 | // Never more than one USEFUL outstanding transaction! 143 | val n_pending_next = n_pending + 144 | Mux(io.bus.ar.ready && io.bus.ar.valid, UInt(1), UInt(0)) - 145 | Mux(io.bus.r.valid, UInt(1), UInt(0)) 146 | n_pending := n_pending_next 147 | when (drop_outstanding) { 148 | n_to_drop := n_pending - Mux(io.bus.r.valid, UInt(1), UInt(0)) 149 | } .elsewhen (n_to_drop =/= UInt(0) && io.bus.r.valid) { 150 | n_to_drop := n_to_drop - UInt(1) 151 | } 152 | 153 | // buffer control path 154 | when (clear_buffer) { 155 | buf_size := UInt(0) 156 | buf_base := io.req.pc 157 | buf_tail := UInt(0) 158 | buf_head := UInt(0) 159 | } .otherwise { 160 | val resp_inst_size = Mux(isRVC(io.resp.bits.inst), UInt(2), UInt(4)) 161 | val base_diff_bytes = Mux(io.resp.fire, resp_inst_size, UInt(0)) 162 | val end_diff_bytes = Mux(expected_bus_fetch_valid, n_useful_bus_bytes, UInt(0)) 163 | val head_diff = base_diff_bytes >> 1 164 | val tail_diff = end_diff_bytes >> 1 165 | buf_head := buf_head + head_diff 166 | buf_base := buf_base + base_diff_bytes 167 | buf_tail := buf_tail + tail_diff 168 | buf_size := min(buf_size + end_diff_bytes - base_diff_bytes, UInt(8)) 169 | } 170 | 171 | val busHasError = io.bus.r.valid && (io.bus.r.bits.resp === AXI4Parameters.RESP_SLVERR || io.bus.r.bits.resp === AXI4Parameters.RESP_DECERR) 172 | // buffer refill writes: 173 | // All replies are already filtered with 'expected_bus_fetch_valid' 174 | // Therefore, when bus reply appears, take it! 175 | when (expected_bus_fetch_valid) { 176 | buf_vec(buf_tail) := bus_first_halfword 177 | // Second half is only undesired after a branch 178 | // In this case, all buffer contents are garbage, so writing wastefully is fine 179 | buf_vec(buf_tail + UInt(1)) := bus_second_halfword 180 | err_vec(buf_tail) := busHasError 181 | err_vec(buf_tail + UInt(1)) := busHasError 182 | } 183 | 184 | // reply management 185 | io.resp.bits.pc := buf_base 186 | when (buf_size === UInt(0)) { 187 | io.resp.valid := expected_bus_fetch_valid && (!jumped_to_halfword_aligned || isRVC(bus_first_halfword)) 188 | io.resp.bits.inst := Cat(bus_second_halfword, bus_first_halfword) 189 | io.resp.bits.error := busHasError 190 | } .elsewhen (buf_size === UInt(2) && !isRVC(buf_vec(buf_head))) { 191 | io.resp.valid := expected_bus_fetch_valid 192 | io.resp.bits.inst := Cat(bus_first_halfword, head_halfword) 193 | io.resp.bits.error := busHasError || err_vec(buf_head) 194 | } .otherwise { 195 | io.resp.valid := Bool(true) 196 | io.resp.bits.inst := Cat(next_halfword, head_halfword) 197 | io.resp.bits.error := err_vec(buf_head) || err_vec(buf_head + UInt(1)) 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/LoadExtender.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Load data extender 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util.Cat 21 | import freechips.rocketchip._ 22 | import rocket._ 23 | import Params._ 24 | 25 | class LoadExtender extends Module { 26 | val io = IO(new Bundle { 27 | val offset = Input(UInt(width = xByteOffsetBitwidth)) 28 | val in = Input(UInt(width = xBitwidth)) 29 | val memType = Input(UInt(width = MT_SZ)) 30 | val out = Output(UInt(width = xBitwidth)) 31 | }) 32 | 33 | val outSigned = Wire(SInt(width = xBitwidth)) 34 | 35 | val inByteSwizzled = Mux(io.offset(0), 36 | Cat(io.in(23,16), io.in(31,24), io.in(7,0), io.in(15,8)), 37 | io.in) 38 | 39 | val inSwizzled = Mux(io.offset(1), 40 | Cat(inByteSwizzled(15,0), inByteSwizzled(31,16)), 41 | inByteSwizzled) 42 | 43 | outSigned := inSwizzled.asSInt 44 | io.out := outSigned.asUInt 45 | 46 | when (io.memType === MT_B) { 47 | outSigned := inSwizzled(7,0).asSInt 48 | } .elsewhen(io.memType === MT_H) { 49 | outSigned := inSwizzled(15,0).asSInt 50 | } .elsewhen(io.memType === MT_BU) { 51 | io.out := inSwizzled(7,0) 52 | } .elsewhen(io.memType === MT_HU) { 53 | io.out := inSwizzled(15,0) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/Params.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // BottleRocket parameters 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.internal.firrtl.Width 21 | import freechips.rocketchip._ 22 | import config.{Config, Field} 23 | import system.TinyConfig 24 | import rocket.{RocketCoreParams, MulDivParams, DCacheParams, ICacheParams} 25 | import amba.axi4.{AXI4BundleParameters} 26 | import tile._ 27 | import coreplex._ 28 | import devices.debug.{DebugModuleParams, DefaultDebugModuleParams} 29 | 30 | package object Params { 31 | val busAddrBitwidth = 32.W 32 | val busSizeBitwidth = 2.W 33 | val busLenBitwidth = 4.W 34 | val busDataBitwidth = 32.W 35 | val busUserBitwidth = 16.W 36 | val busIDBitwidth = 16.W 37 | 38 | val xBitwidth = 32.W 39 | val halfXBitwidth = 16.W 40 | val xByteOffsetBitwidth = 2.W 41 | val instBitwidth = 32.W 42 | val nRegisters = 32 43 | val axiParams = new AXI4BundleParameters(32, 32, 16, 16) 44 | } 45 | 46 | case object NMI_VEC extends Field[BigInt] 47 | 48 | class TileConfig extends Config((site, here, up) => { 49 | case TileKey => RocketTileParams( 50 | core = RocketCoreParams( 51 | useUser = true, 52 | useVM = false, 53 | useAtomics = false, 54 | nBreakpoints = 4, 55 | nPMPs = 0, 56 | fpu = None, 57 | mulDiv = Some(MulDivParams(mulUnroll = 8))), 58 | btb = None, 59 | dcache = Some(DCacheParams()), 60 | icache = Some(ICacheParams())) 61 | case BuildRoCC => false 62 | case DebugModuleParams => DefaultDebugModuleParams(site(XLen)) 63 | case NMI_VEC => BigInt("100", 16) 64 | }) 65 | 66 | class DefaultBottleRocketConfig extends Config(new TileConfig ++ new TinyConfig) 67 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/RVFI.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Chisel Bundle implementation of RISC-V Formal Interface (RVFI) 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import Params._ 21 | 22 | class RVFI extends Bundle { 23 | val valid = Output(Bool()) 24 | val order = Output(UInt(64.W)) 25 | val insn = Output(UInt(32.W)) 26 | val trap = Output(Bool()) 27 | val halt = Output(Bool()) 28 | val intr = Output(Bool()) 29 | val rs1_addr = Output(UInt(5.W)) 30 | val rs1_rdata = Output(UInt(xBitwidth)) 31 | val rs2_addr = Output(UInt(5.W)) 32 | val rs2_rdata = Output(UInt(xBitwidth)) 33 | val rd_addr = Output(UInt(5.W)) 34 | val rd_wdata = Output(UInt(xBitwidth)) 35 | val pc_rdata = Output(UInt(xBitwidth)) 36 | val pc_wdata = Output(UInt(xBitwidth)) 37 | val mem_addr = Output(UInt(xBitwidth)) 38 | val mem_rmask = Output(UInt(4.W)) 39 | val mem_wmask = Output(UInt(4.W)) 40 | val mem_rdata = Output(UInt(xBitwidth)) 41 | val mem_wdata = Output(UInt(xBitwidth)) 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/bottlerocket/RegfileWithZero.scala: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This flop-based regfile has register number zero hardwired to zero 16 | 17 | package bottlerocket 18 | 19 | import chisel3._ 20 | import chisel3.util._ 21 | import Params._ 22 | import chisel3.internal.firrtl.Width 23 | 24 | class RegfileWithZero(nRegs: Int, regWidth: Width, name: String = "regfileReverseIdx") { 25 | // nRegs must be positive power of two 26 | Predef.assert(nRegs > 0) 27 | Predef.assert((nRegs & (nRegs - 1)) == 0) 28 | private val idxMSB = log2Ceil(nRegs) - 1 29 | private val regs = Mem(nRegs-1, UInt(width = regWidth)) 30 | regs.suggestName(name) 31 | private def toIndex(addr: UInt) = ~addr(idxMSB,0) 32 | def read(addr: UInt) = Mux(addr === 0.U, 0.U, regs.read(toIndex(addr))) 33 | def write(addr: UInt, data: UInt) = { 34 | when (addr =/= 0.U) { 35 | regs.write(toIndex(addr), data) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/BottleRocketCoreTB.v: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Unit-level testbench for the BottleRocket core with RISC-V isa tests 16 | 17 | `include "BottleRocketCore.v" 18 | `include "MockAXI4LiteSRAM.v" 19 | 20 | `define MAXCYCLES 100000 21 | `define TOHOSTADDR 'h00006000 22 | `define SUCCESSCODE 1 23 | 24 | module BottleRocketCoreTB( 25 | ); 26 | 27 | reg clk; 28 | reg reset; 29 | 30 | wire imem_awvalid; 31 | wire imem_awready; 32 | wire [31:0] imem_awaddr; 33 | wire [2:0] imem_awprot; 34 | wire [3:0] imem_awcache; 35 | wire imem_wvalid; 36 | wire imem_wready; 37 | wire [31:0] imem_wdata; 38 | wire [3:0] imem_wstrb; 39 | wire imem_bvalid; 40 | wire imem_bready; 41 | wire [1:0] imem_bresp; 42 | wire imem_arvalid; 43 | wire imem_arready; 44 | wire [31:0] imem_araddr; 45 | wire [2:0] imem_arprot; 46 | wire [3:0] imem_arcache; 47 | wire imem_rvalid; 48 | wire imem_rready; 49 | wire [1:0] imem_rresp; 50 | wire [31:0] imem_rdata; 51 | 52 | 53 | wire dmem_awvalid; 54 | wire dmem_awready; 55 | wire [31:0] dmem_awaddr; 56 | wire [2:0] dmem_awprot; 57 | wire [3:0] dmem_awcache; 58 | wire dmem_wvalid; 59 | wire dmem_wready; 60 | wire [31:0] dmem_wdata; 61 | wire [3:0] dmem_wstrb; 62 | wire dmem_bvalid; 63 | wire dmem_bready; 64 | wire [1:0] dmem_bresp; 65 | wire dmem_arvalid; 66 | wire dmem_arready; 67 | wire [31:0] dmem_araddr; 68 | wire [2:0] dmem_arprot; 69 | wire [3:0] dmem_arcache; 70 | wire dmem_rvalid; 71 | wire dmem_rready; 72 | wire [1:0] dmem_rresp; 73 | wire [31:0] dmem_rdata; 74 | 75 | MockAXI4LiteSRAM imem( 76 | .aclk(clk), 77 | .aresetn(~reset), 78 | .awvalid(imem_awvalid), 79 | .awready(imem_awready), 80 | .awaddr(imem_awaddr), 81 | .awprot(imem_awprot), 82 | .awcache(imem_awcache), 83 | .wvalid(imem_wvalid), 84 | .wready(imem_wready), 85 | .wdata(imem_wdata), 86 | .wstrb(imem_wstrb), 87 | .bvalid(imem_bvalid), 88 | .bready(imem_bready), 89 | .bresp(imem_bresp), 90 | .arvalid(imem_arvalid), 91 | .arready(imem_arready), 92 | .araddr(imem_araddr), 93 | .arprot(imem_arprot), 94 | .arcache(imem_arcache), 95 | .rvalid(imem_rvalid), 96 | .rready(imem_rready), 97 | .rresp(imem_rresp), 98 | .rdata(imem_rdata) 99 | ); 100 | 101 | MockAXI4LiteSRAM dmem( 102 | .aclk(clk), 103 | .aresetn(~reset), 104 | .awvalid(dmem_awvalid), 105 | .awready(dmem_awready), 106 | .awaddr(dmem_awaddr), 107 | .awprot(dmem_awprot), 108 | .awcache(dmem_awcache), 109 | .wvalid(dmem_wvalid), 110 | .wready(dmem_wready), 111 | .wdata(dmem_wdata), 112 | .wstrb(dmem_wstrb), 113 | .bvalid(dmem_bvalid), 114 | .bready(dmem_bready), 115 | .bresp(dmem_bresp), 116 | .arvalid(dmem_arvalid), 117 | .arready(dmem_arready), 118 | .araddr(dmem_araddr), 119 | .arprot(dmem_arprot), 120 | .arcache(dmem_arcache), 121 | .rvalid(dmem_rvalid), 122 | .rready(dmem_rready), 123 | .rresp(dmem_rresp), 124 | .rdata(dmem_rdata) 125 | ); 126 | 127 | BottleRocketCore core( 128 | .clock(clk), 129 | .reset(reset), 130 | .io_constclk(clk), 131 | .io_nmi(1'b0), 132 | .io_eip(1'b0), 133 | .io_dmi_req_ready(), 134 | .io_dmi_req_valid(1'b0), 135 | .io_dmi_req_bits_addr(7'b0), 136 | .io_dmi_req_bits_data(32'b0), 137 | .io_dmi_req_bits_op(2'b0), 138 | .io_dmi_resp_ready(1'b0), 139 | .io_dmi_resp_valid(), 140 | .io_dmi_resp_bits_data(), 141 | .io_dmi_resp_bits_resp(), 142 | .io_wfisleep(), 143 | .io_traceInst(), 144 | .io_traceRetire(), 145 | .io_traceInterrupt(), 146 | .io_traceEret(), 147 | .io_iBus_aw_valid(imem_awvalid), 148 | .io_iBus_aw_ready(imem_awready), 149 | .io_iBus_aw_bits_addr(imem_awaddr), 150 | .io_iBus_aw_bits_prot(imem_awprot), 151 | .io_iBus_aw_bits_cache(imem_awcache), 152 | .io_iBus_w_valid(imem_wvalid), 153 | .io_iBus_w_ready(imem_wready), 154 | .io_iBus_w_bits_data(imem_wdata), 155 | .io_iBus_w_bits_strb(imem_wstrb), 156 | .io_iBus_b_valid(imem_bvalid), 157 | .io_iBus_b_ready(imem_bready), 158 | .io_iBus_b_bits_resp(imem_bresp), 159 | .io_iBus_ar_valid(imem_arvalid), 160 | .io_iBus_ar_ready(imem_arready), 161 | .io_iBus_ar_bits_addr(imem_araddr), 162 | .io_iBus_ar_bits_prot(imem_arprot), 163 | .io_iBus_ar_bits_cache(imem_arcache), 164 | .io_iBus_r_valid(imem_rvalid), 165 | .io_iBus_r_ready(imem_rready), 166 | .io_iBus_r_bits_resp(imem_rresp), 167 | .io_iBus_r_bits_data(imem_rdata), 168 | .io_dBus_aw_valid(dmem_awvalid), 169 | .io_dBus_aw_ready(dmem_awready), 170 | .io_dBus_aw_bits_addr(dmem_awaddr), 171 | .io_dBus_aw_bits_prot(dmem_awprot), 172 | .io_dBus_aw_bits_cache(dmem_awcache), 173 | .io_dBus_w_valid(dmem_wvalid), 174 | .io_dBus_w_ready(dmem_wready), 175 | .io_dBus_w_bits_data(dmem_wdata), 176 | .io_dBus_w_bits_strb(dmem_wstrb), 177 | .io_dBus_b_valid(dmem_bvalid), 178 | .io_dBus_b_ready(dmem_bready), 179 | .io_dBus_b_bits_resp(dmem_bresp), 180 | .io_dBus_ar_valid(dmem_arvalid), 181 | .io_dBus_ar_ready(dmem_arready), 182 | .io_dBus_ar_bits_addr(dmem_araddr), 183 | .io_dBus_ar_bits_prot(dmem_arprot), 184 | .io_dBus_ar_bits_cache(dmem_arcache), 185 | .io_dBus_r_valid(dmem_rvalid), 186 | .io_dBus_r_ready(dmem_rready), 187 | .io_dBus_r_bits_resp(dmem_rresp), 188 | .io_dBus_r_bits_data(dmem_rdata) 189 | ); 190 | 191 | 192 | integer ncycles; 193 | 194 | reg [1023:0] image; 195 | 196 | initial begin 197 | if ($value$plusargs("image=%s", image)) begin 198 | $readmemh({image,".hex"}, imem.mem); 199 | $readmemh({image,".hex"}, dmem.mem); 200 | $shm_open({image,".dump.d"}); 201 | end else begin 202 | $fatal; 203 | end 204 | $shm_probe("AMC"); 205 | $recordvars(core); 206 | ncycles = 0; 207 | clk = 1'b0; 208 | reset = 1'b1; 209 | repeat(20) #5 clk = ~clk; 210 | reset = 1'b0; 211 | forever #5 clk = ~clk; 212 | end 213 | 214 | always @(posedge clk) begin 215 | ncycles = ncycles + 1; 216 | if (ncycles > `MAXCYCLES) begin 217 | $info("Failure: timeout!\n"); 218 | $shm_close; 219 | $fatal; 220 | end 221 | end 222 | 223 | always @(posedge clk) begin 224 | if (dmem_awvalid && dmem_wvalid && dmem_awaddr == `TOHOSTADDR) begin 225 | if (dmem_wdata == `SUCCESSCODE) begin 226 | $info("Success!\n"); 227 | $shm_close; 228 | $finish; 229 | end else begin 230 | $info("Failure!\n"); 231 | $shm_close; 232 | $fatal; 233 | end 234 | end 235 | end 236 | 237 | endmodule 238 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | RISCV_TEST_BASE = ../third_party/rocket-chip/riscv-tools/riscv-tests 16 | GEN_SRC_DIR = ../generated-src 17 | 18 | RISCV_GCC = riscv32-unknown-elf-gcc 19 | RISCV_GCC_OPTS = -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles 20 | RISCV_OBJDUMP = riscv32-unknown-elf-objdump 21 | 22 | NCVERILOG_OPTS = +incdir+$(GEN_SRC_DIR) +sv +nctimescale+1ns/1ns +access+r +nclicq 23 | 24 | SIM ?= ncverilog 25 | SIM_OPTS ?= $(NCVERILOG_OPTS) 26 | 27 | # fence_i not supported for Harvard architecture 28 | RV32UI_TESTS = addi \ 29 | add \ 30 | andi \ 31 | and \ 32 | auipc \ 33 | beq \ 34 | bge \ 35 | bgeu \ 36 | blt \ 37 | bltu \ 38 | bne \ 39 | jalr \ 40 | jal \ 41 | lb \ 42 | lbu \ 43 | lh \ 44 | lhu \ 45 | lui \ 46 | lw \ 47 | ori \ 48 | or \ 49 | sb \ 50 | sh \ 51 | simple \ 52 | slli \ 53 | sll \ 54 | slti \ 55 | sltiu \ 56 | slt \ 57 | sltu \ 58 | srai \ 59 | sra \ 60 | srli \ 61 | srl \ 62 | sub \ 63 | sw \ 64 | xori \ 65 | xor 66 | 67 | RV32MI_TESTS = breakpoint \ 68 | csr \ 69 | illegal \ 70 | ma_addr \ 71 | ma_fetch \ 72 | mcsr \ 73 | sbreak \ 74 | scall \ 75 | shamt 76 | 77 | TESTS = $(RV32UI_TESTS) # $(RV32MI_TESTS) 78 | 79 | default: $(addsuffix .log, $(TESTS)) 80 | 81 | %.log: %.hex %.elf $(GEN_SRC_DIR)/*.v *.v 82 | $(RISCV_OBJDUMP) -D $*.elf > $*.objdump 83 | $(SIM) $(SIM_OPTS) +image=$* BottleRocketCoreTB.v &> $*.log 84 | 85 | %.hex: %.elf 86 | elf2hex 4 32768 $< > $@ 87 | 88 | %.elf: $(RISCV_TEST_BASE)/isa/rv32ui/%.S 89 | $(RISCV_GCC) $(RISCV_GCC_OPTS) -I$(RISCV_TEST_BASE)/env/p -I$(RISCV_TEST_BASE)/isa/macros/scalar -Ttest.ld -o $@ $< 90 | 91 | %.elf: $(RISCV_TEST_BASE)/isa/rv32mi/%.S 92 | $(RISCV_GCC) $(RISCV_GCC_OPTS) -I$(RISCV_TEST_BASE)/env/p -I$(RISCV_TEST_BASE)/isa/macros/scalar -Ttest.ld -o $@ $< 93 | 94 | .SECONDARY: $(addsuffix .elf, $(TESTS)) $(addsuffix .hex, $(TESTS)) 95 | 96 | .PHONY: clean 97 | 98 | clean: 99 | rm -rf *.hex *.elf *.log ncverilog* INCA_libs data *.log *.dsn *.trn *.dump.d 100 | -------------------------------------------------------------------------------- /test/MockAXI4LiteSRAM.v: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Mock AXI4Lite SRAM for unit testing the BottleRocket core 16 | 17 | module MockAXI4LiteSRAM ( 18 | input aclk, 19 | input aresetn, 20 | input awvalid, 21 | output logic awready, 22 | input [31:0] awaddr, 23 | input [2:0] awprot, 24 | input [3:0] awcache, 25 | input wvalid, 26 | output wready, 27 | input [31:0] wdata, 28 | input [3:0] wstrb, 29 | output logic bvalid, 30 | input bready, 31 | output [1:0] bresp, 32 | input arvalid, 33 | output logic arready, 34 | input [31:0] araddr, 35 | input [2:0] arprot, 36 | input [3:0] arcache, 37 | output logic rvalid, 38 | input rready, 39 | output [1:0] rresp, 40 | output logic [31:0] rdata 41 | ); 42 | 43 | logic [31:0] mem [0:32767]; 44 | 45 | logic write_active; 46 | logic [3:0] write_resp_delay; 47 | logic [3:0] write_strb; 48 | logic [31:0] write_data; 49 | logic [31:0] write_addr; 50 | 51 | logic read_active; 52 | logic [3:0] read_resp_delay; 53 | logic [31:0] read_data; 54 | logic [31:0] read_addr; 55 | 56 | // Writes 57 | // TODO (amagyar): handle awvalid / wvalid independently 58 | 59 | always @(posedge aclk) begin 60 | if (awvalid && awready && wvalid && wready) begin 61 | write_resp_delay <= $random; 62 | end else if (write_active && write_resp_delay != 0) begin 63 | write_resp_delay <= write_resp_delay - 1; 64 | end 65 | end 66 | 67 | always @(posedge aclk) begin 68 | if (!aresetn) begin 69 | write_active <= 1'b0; 70 | end else if (awvalid && awready && wvalid && wready) begin 71 | write_active <= 1'b1; 72 | end else if (bvalid && bready) begin 73 | write_active <= 1'b0; 74 | end 75 | end // always @ (posedge aclk) 76 | 77 | always @(posedge aclk) begin 78 | if (awvalid && awready && wvalid && wready) begin 79 | if (wstrb[0]) 80 | mem[awaddr[31:2]][7:0] <= wdata[7:0]; 81 | if (wstrb[1]) 82 | mem[awaddr[31:2]][15:8] <= wdata[15:8]; 83 | if (wstrb[2]) 84 | mem[awaddr[31:2]][23:16] <= wdata[23:16]; 85 | if (wstrb[3]) 86 | mem[awaddr[31:2]][31:24] <= wdata[31:24]; 87 | end 88 | end 89 | 90 | assign awready = !write_active || (bready && bvalid); 91 | assign wready = !write_active || (bready && bvalid); 92 | assign bvalid = write_active && write_resp_delay == 0; 93 | assign bresp = 2'b0; 94 | 95 | // Reads 96 | 97 | always @(posedge aclk) begin 98 | if (arvalid && arready) begin 99 | read_resp_delay <= $random; 100 | end else if (read_active && read_resp_delay != 0) begin 101 | read_resp_delay <= read_resp_delay - 1; 102 | end 103 | end 104 | 105 | always @(posedge aclk) begin 106 | if (!aresetn) begin 107 | read_active <= 1'b0; 108 | end else if (arvalid && arready) begin 109 | read_active <= 1'b1; 110 | read_addr <= araddr; 111 | end else if (rvalid && rready) begin 112 | read_active <= 1'b0; 113 | end 114 | end // always @ (posedge aclk) 115 | 116 | always @(*) begin 117 | rdata = mem[read_addr[31:2]]; 118 | end 119 | 120 | assign arready = !read_active || (rready && rvalid); 121 | assign rvalid = read_active && read_resp_delay == 0; 122 | assign rresp = 2'b0; 123 | 124 | endmodule // MockAXI4LiteSRAM 125 | -------------------------------------------------------------------------------- /test/test.ld: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | */ 15 | 16 | OUTPUT_ARCH( "riscv" ) 17 | ENTRY(_start) 18 | 19 | SECTIONS 20 | { 21 | . = 0x0004100; 22 | .text.init : AT(ADDR(.text.init)) { *(.text.init) } 23 | .tohost ALIGN(0x2000) : AT(ADDR(.tohost)) { *(.tohost) } 24 | .text ALIGN(0x1000) : AT(ADDR(.text)) { *(.text) } 25 | .data ALIGN(0x1000) : AT(ADDR(.data)) { *(.data) } 26 | .bss : AT(ADDR(.bss)) { *(.bss) } 27 | _end = .; 28 | } 29 | 30 | --------------------------------------------------------------------------------