├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── AUTHORS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── DEVNOTES.md ├── FEATURES_LIMITATIONS.md ├── LICENSE ├── LICENSE.Nic30 ├── LICENSE.firrtl ├── MAINTAINERS ├── README.md ├── build.sbt ├── helpers └── src │ ├── main │ └── scala │ │ ├── HwEnum.scala │ │ ├── MemInit.scala │ │ ├── bundleconvert.scala │ │ ├── tools │ │ ├── ChiselGen.scala │ │ ├── DeepWhen.scala │ │ ├── ModulePreset.scala │ │ ├── ModuleRename.scala │ │ └── ParamWrapperGenerator.scala │ │ └── vecconvert.scala │ └── test │ └── scala │ ├── HwEnumSpec.scala │ ├── MemInitSpec.scala │ ├── bundleconvertSpec.scala │ ├── tools │ ├── ChiselGenSpec.scala │ ├── DeepWhenSpec.scala │ └── ParamWrapperGeneratorSpec.scala │ ├── utils │ └── VerilogMatchers.scala │ └── vecconvertSpec.scala ├── mrproper.sh ├── project ├── build.properties ├── plugins.sbt └── sbt-antlr4.sbt ├── src ├── main │ ├── antlr4 │ │ ├── sv2017Lexer.g4 │ │ └── sv2017Parser.g4 │ ├── resources │ │ └── project │ │ │ ├── config.yml │ │ │ └── hdl │ │ │ ├── my_module.sv │ │ │ └── my_package.sv │ └── scala │ │ ├── logger │ │ ├── EasyLogger.scala │ │ ├── InfoLogger.scala │ │ ├── Logger.scala │ │ └── LoggerOptions.scala │ │ └── sv2chisel │ │ ├── AppExample.scala │ │ ├── Chiselizer.scala │ │ ├── Driver.scala │ │ ├── Emitter.scala │ │ ├── Implicits.scala │ │ ├── Main.scala │ │ ├── Parser.scala │ │ ├── ParserLogging.scala │ │ ├── Project.scala │ │ ├── RaiseUnsupported.scala │ │ ├── SV2ChiselException.scala │ │ ├── TranslationOptions.scala │ │ ├── Utils.scala │ │ ├── Visitor.scala │ │ ├── ir │ │ ├── EvalExpression.scala │ │ ├── ExpressionToLiteral.scala │ │ ├── ExpressionWidth.scala │ │ ├── IR.scala │ │ ├── PrimOps.scala │ │ ├── RefreshTypes.scala │ │ ├── WidthExpressionTypes.scala │ │ └── ir_package.scala │ │ └── transforms │ │ ├── AddDontCare.scala │ │ ├── CheckBlockingAssignments.scala │ │ ├── CheckScopes.scala │ │ ├── CheckUseBeforeDecl.scala │ │ ├── FixFunctionImplicitReturns.scala │ │ ├── FixReservedNames.scala │ │ ├── FlowReferences.scala │ │ ├── InferDefLogicClocks.scala │ │ ├── InferParamTypes.scala │ │ ├── InferUInts.scala │ │ ├── LegalizeExpressions.scala │ │ ├── LegalizeParamDefaults.scala │ │ ├── NameInstancePorts.scala │ │ ├── PropagateClocks.scala │ │ ├── RemoveConcats.scala │ │ ├── RemovePatterns.scala │ │ ├── ToCamelCase.scala │ │ ├── Transform.scala │ │ └── TypeReferences.scala └── test │ └── scala │ ├── AddDontCareSpec.scala │ ├── AsUIntSubRangeSpec.scala │ ├── BasicSpec.scala │ ├── BlackBoxSpec.scala │ ├── BundleTypeSpec.scala │ ├── ComplexParamSpec.scala │ ├── ComplexRangeSpec.scala │ ├── DefLogicSpec.scala │ ├── EmitterSpec.scala │ ├── EnumSpec.scala │ ├── FixReservedNamesSpec.scala │ ├── FunctionSpec.scala │ ├── IfElseGenSpec.scala │ ├── IfElseSpec.scala │ ├── InstancesFlowSpec.scala │ ├── LegalizeExpressionSpec.scala │ ├── LiteralSpec.scala │ ├── MacroSpecs.scala │ ├── ModuleSpec.scala │ ├── PackageSpec.scala │ ├── PatternSpec.scala │ ├── RemoveConcatSpec.scala │ ├── StringSpec.scala │ ├── SwitchSpec.scala │ ├── ToCamelCaseSpec.scala │ ├── UnpackedEmissionStyleSpec.scala │ ├── WireOrRegSpec.scala │ └── utils │ ├── ChiselMatchers.scala │ └── Sv2ChiselSpec.scala ├── utils └── bin │ └── sv2chisel └── version.sbt /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Please provide a minimal verilog example causing the issue 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | 20 | **Additional context** 21 | Add any other context about the problem here. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | chisel_gen/* 2 | test_run_dir/* 3 | utils/bin/sv2chisel.jar 4 | *.dot 5 | *.svg 6 | *.v 7 | 8 | # VS CODE with metals and bloop 9 | .bloop/ 10 | .bsp/ 11 | .metals/ 12 | project/.bloop/ 13 | project/metals.sbt 14 | project/project/ 15 | 16 | # SBT windows 17 | null/ 18 | 19 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Scala.gitignore 20 | 21 | *.class 22 | *.log 23 | 24 | 25 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Global/SBT.gitignore 26 | 27 | # Simple Build Tool 28 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 29 | 30 | dist/* 31 | target/ 32 | lib_managed/ 33 | src_managed/ 34 | project/boot/ 35 | project/plugins/project/ 36 | .history 37 | .cache 38 | .lib/ 39 | 40 | 41 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Global/Ensime.gitignore 42 | 43 | # Ensime specific 44 | .ensime 45 | .ensime_cache/ 46 | .ensime_lucene/ 47 | 48 | 49 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Global/macOS.gitignore 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | 60 | # Thumbnails 61 | ._* 62 | 63 | # Files that might appear in the root of a volume 64 | .DocumentRevisions-V100 65 | .fseventsd 66 | .Spotlight-V100 67 | .TemporaryItems 68 | .Trashes 69 | .VolumeIcon.icns 70 | .com.apple.timemachine.donotpresent 71 | 72 | # Directories potentially created on remote AFP share 73 | .AppleDB 74 | .AppleDesktop 75 | Network Trash Folder 76 | Temporary Items 77 | .apdisk 78 | 79 | 80 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Global/Vim.gitignore 81 | 82 | # Swap 83 | [._]*.s[a-v][a-z] 84 | [._]*.sw[a-p] 85 | [._]s[a-v][a-z] 86 | [._]sw[a-p] 87 | 88 | # Session 89 | Session.vim 90 | 91 | # Temporary 92 | .netrwhist 93 | *~ 94 | # Auto-generated tag files 95 | tags 96 | 97 | 98 | ### https://raw.github.com/github/gitignore/ade08baee2cf60afd794432b3e779fe5844c545d/Global/Emacs.gitignore 99 | 100 | # -*- mode: gitignore; -*- 101 | *~ 102 | \#*\# 103 | /.emacs.desktop 104 | /.emacs.desktop.lock 105 | *.elc 106 | auto-save-list 107 | tramp 108 | .\#* 109 | 110 | # Org-mode 111 | .org-id-locations 112 | *_archive 113 | 114 | # flymake-mode 115 | *_flymake.* 116 | 117 | # eshell files 118 | /eshell/history 119 | /eshell/lastdir 120 | 121 | # elpa packages 122 | /elpa/ 123 | 124 | # reftex files 125 | *.rel 126 | 127 | # AUCTeX auto folder 128 | /auto/ 129 | 130 | # cask packages 131 | .cask/ 132 | dist/ 133 | 134 | # Flycheck 135 | flycheck_*.el 136 | 137 | # server auth directory 138 | /server/ 139 | 140 | # projectiles files 141 | .projectile 142 | 143 | # directory configuration 144 | .dir-locals.el 145 | 146 | 147 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of sv2chisel authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files 3 | # and it lists the copyright holders only. 4 | 5 | # Names should be added to this file as one of 6 | # Organization's name 7 | # Individual's name 8 | # Individual's name 9 | # See CONTRIBUTORS for the meaning of multiple email addresses. 10 | 11 | # Please keep the list sorted. 12 | 13 | OVH SAS -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to sv2chisel 2 | 3 | This project accepts contributions. In order to contribute, you should 4 | pay attention to a few things: 5 | 6 | 1. your code must follow the coding style rules 7 | 2. your code must be unit-tested 8 | 3. your code must be documented 9 | 4. your work must be signed (see below) 10 | 5. you may contribute through GitHub Pull Requests 11 | 12 | # Coding and documentation Style 13 | 14 | Please refer to [Scala style guide](https://docs.scala-lang.org/style/) as a reference. 15 | 16 | # Submitting Modifications 17 | 18 | The contributions should be submitted through Github Pull Requests 19 | and follow the DCO which is defined below. 20 | 21 | # Licensing for new files 22 | 23 | sv2chisel is licensed under a BSD-3-Clause license. Anything 24 | contributed to sv2chisel must be released under this license. 25 | 26 | When introducing a new file into the project, please make sure it has a 27 | copyright header making clear under which license it's being released. 28 | 29 | # Developer Certificate of Origin (DCO) 30 | 31 | To improve tracking of contributions to this project we will use a 32 | process modeled on the modified DCO 1.1 and use a "sign-off" procedure 33 | on patches that are being emailed around or contributed in any other 34 | way. 35 | 36 | The sign-off is a simple line at the end of the explanation for the 37 | patch, which certifies that you wrote it or otherwise have the right 38 | to pass it on as an open-source patch. The rules are pretty simple: 39 | if you can certify the below: 40 | 41 | By making a contribution to this project, I certify that: 42 | 43 | (a) The contribution was created in whole or in part by me and I have 44 | the right to submit it under the open source license indicated in 45 | the file; or 46 | 47 | (b) The contribution is based upon previous work that, to the best of 48 | my knowledge, is covered under an appropriate open source License 49 | and I have the right under that license to submit that work with 50 | modifications, whether created in whole or in part by me, under 51 | the same open source license (unless I am permitted to submit 52 | under a different license), as indicated in the file; or 53 | 54 | (c) The contribution was provided directly to me by some other person 55 | who certified (a), (b) or (c) and I have not modified it. 56 | 57 | (d) The contribution is made free of any other party's intellectual 58 | property claims or rights. 59 | 60 | (e) I understand and agree that this project and the contribution are 61 | public and that a record of the contribution (including all 62 | personal information I submit with it, including my sign-off) is 63 | maintained indefinitely and may be redistributed consistent with 64 | this project or the open source license(s) involved. 65 | 66 | 67 | then you just add a line saying 68 | 69 | Signed-off-by: Random J Developer 70 | 71 | using your real name (sorry, no pseudonyms or anonymous contributions.) -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the sv2chisel repository. 3 | # 4 | # Names should be added to this file only after verifying that 5 | # the individual or the individual's organization has agreed to 6 | # the appropriate CONTRIBUTING.md file. 7 | # 8 | # Names should be added to this file like so: 9 | # Individual's name 10 | # Individual's name 11 | # 12 | # Please keep the list sorted. 13 | # 14 | 15 | Jean Bruant @johnsbrew jean.bruant@ovhcloud.com 16 | Pierre-Henri Horrein pierre-henri.horrein@ovhcloud.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, OVH SAS 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /LICENSE.Nic30: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nic30 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.firrtl: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 - 2020 The Regents of the University of 2 | California (Regents). All Rights Reserved. Redistribution and use in 3 | source and binary forms, with or without modification, are permitted 4 | provided that the following conditions are met: 5 | * Redistributions of source code must retain the above 6 | copyright notice, this list of conditions and the following 7 | two paragraphs of disclaimer. 8 | * Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | two paragraphs of disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | * Neither the name of the Regents nor the names of its contributors 13 | may be used to endorse or promote products derived from this 14 | software without specific prior written permission. 15 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 16 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, 17 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 18 | REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF 22 | ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION 23 | TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 24 | MODIFICATIONS. -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | # This is the official list of the sv2chisel project maintainers. 2 | # This is mostly useful for contributors that want to push 3 | # significant pull requests or for project management issues. 4 | # 5 | # 6 | # Names should be added to this file like so: 7 | # Individual's name 8 | # Individual's name 9 | # 10 | # Please keep the list sorted. 11 | # 12 | 13 | Jean Bruant @johnsbrew jean.bruant@ovhcloud.com -------------------------------------------------------------------------------- /helpers/src/main/scala/HwEnum.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel.helpers 6 | package enum 7 | 8 | import chisel3._ 9 | 10 | // require import scala.language.implicitConversions if not enabled in scalacOptions 11 | abstract class GenericHwEnum extends Enumeration { 12 | def apply(): UInt = UInt(util.log2Up(this.values.size).W) 13 | implicit def valueToUInt(x: Value): UInt = x.id.U 14 | } 15 | 16 | // Typical use-case example 17 | private object GenericHwEnumExample extends GenericHwEnum { 18 | val stateA = Value 19 | val stateB = Value 20 | val stateC = Value 21 | } 22 | private object GenericHwEnumInlineExample extends GenericHwEnum { 23 | val stateA, stateB, stateC = Value 24 | } 25 | 26 | 27 | // require import scala.language.implicitConversions if not enabled in scalacOptions 28 | abstract class CustomHwEnum extends Enumeration { 29 | def getWidth: Int = this.values.map(_.getWidth).max 30 | def apply(): UInt = UInt(this.getWidth.W) 31 | 32 | protected case class V(data: Data) extends super.Val {} 33 | implicit def valueToCustomVal(x: Value): V = x.asInstanceOf[V] 34 | implicit def valueToUInt(x: Value): UInt = x.data.asUInt 35 | } 36 | 37 | private object CustomHwEnumExample extends CustomHwEnum { 38 | val stateA = V(0.U) 39 | val stateB = V(12.U) 40 | val stateC = V(5.U) 41 | } 42 | 43 | -------------------------------------------------------------------------------- /helpers/src/main/scala/MemInit.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel.helpers 6 | 7 | import chisel3.{Mem, Vec, Data} 8 | 9 | import firrtl.annotations.{MemoryArrayInitAnnotation, MemoryScalarInitAnnotation} 10 | import chisel3.experimental.{ChiselAnnotation, annotate, DataMirror} 11 | 12 | 13 | object MemInit { 14 | /*** INIT FROM DATA (expecting literals) **/ 15 | 16 | // providing similar syntax to VecInit 17 | def apply[T <: Data](initLiteral: T, initLiterals: T*): Mem[T] = { 18 | apply(initLiteral +: initLiterals) 19 | } 20 | // providing similar syntax to VecInit 21 | def apply[T <: Data](initLiterals: Seq[T]): Mem[T] = { 22 | doApply(initLiterals) 23 | } 24 | 25 | private def doApply[T <: Data](initLiterals: Seq[T]): Mem[T] = { 26 | require(!initLiterals.isEmpty, "MemInit requires at least element: cannot create zero-sized memories") 27 | val tpe = initLiterals.head 28 | initLiterals.foreach(t => { 29 | require(DataMirror.checkTypeEquivalence(t, tpe), s"All MemInit elements must share a common type ($tpe selected)") 30 | require(t.isLit, s"All MemInit elements must be literals. Failing at: $t") 31 | }) 32 | apply(initLiterals.length, tpe, initLiterals.map(_.litValue)) 33 | } 34 | 35 | // allow syntax similar to RegInit(VecInit(...)) => MemInit(VecInit()) 36 | def apply[T <: Data](v: Vec[T]): Mem[T] = { 37 | doApply(v.toSeq) // unexpected hanging with scala 2.13.+ requires to use this do_apply intermediate 38 | } 39 | 40 | /*** INIT FROM BigInt **/ 41 | 42 | def apply[T <: Data](size: Int, tpe: T, inits: Seq[BigInt]): Mem[T] = { 43 | require(size == inits.length, "Init vector shall provide a value for each element of the memory") 44 | val m = Mem(size, tpe.cloneType) 45 | annotate(new ChiselAnnotation { 46 | override def toFirrtl = MemoryArrayInitAnnotation(m.toTarget, values = inits) 47 | }) 48 | m 49 | } 50 | 51 | def fill[T <: Data](size: Int, tpe: T)(eltInit: => BigInt): Mem[T] = { 52 | val m = Mem(size, tpe.cloneType) 53 | annotate(new ChiselAnnotation { 54 | override def toFirrtl = MemoryScalarInitAnnotation(m.toTarget, value = eltInit) 55 | }) 56 | m 57 | } 58 | 59 | 60 | def tabulate[T <: Data](size: Int, tpe: T)(f: Int => BigInt): Mem[T] = { 61 | apply(size, tpe, (0 until size).map(f)) 62 | } 63 | 64 | } 65 | 66 | -------------------------------------------------------------------------------- /helpers/src/main/scala/bundleconvert.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel.helpers 6 | 7 | import chisel3._ 8 | import chisel3.util.Cat 9 | 10 | /** Package holding all implicit conversion for bundle range assignments 11 | * 12 | * This package must be imported if subrange access to bundle are made. 13 | * It is a very hacky way of accessing to bundle fields, inherited from inability of verilog to enumerate fields. 14 | * Proper manual translation to chisel 15 | * 16 | * In all bundleconvert package, we use Chisel3 convention: (high, low) slicing, high is included. 17 | * 18 | */ 19 | package object bundleconvert { 20 | implicit def bundleToSubRangeAccess[T <: Record](b: T): RecordSubRangeAccess[T] = new RecordSubRangeAccess(b) 21 | implicit def bundleFieldToData[T <: Record](f: RecordField[T]): Data = f.data 22 | implicit def selectedRecordFieldToUInt[T <: Record](f: SelectedRecordFields[T]): UInt = f.asUInt 23 | } 24 | 25 | object RecordConvertUsageWarning { 26 | private var emitted = false 27 | def emit(s: String): Unit = { 28 | if (!emitted) { 29 | emitted = true 30 | println("[WARNING] Use of bundle subrange access (provided by sv2chisel.helpers.bundleconvert) " + 31 | "is highly discouraged! It is a very hacky way of accessing bundle fields. This hack is provided " + 32 | "to enable flawless 1-1 translation from verilog which does not provide ability to enumerate struct fields. " + 33 | "This code should be refactored to leverage Chisel generation abilities, in this case Record.elements" 34 | ) 35 | // To do ? add pointer to file:loc ? (using stacktrace) 36 | println(s"[INFO] Recording only a single example: $s") 37 | } 38 | } 39 | } 40 | 41 | /** Buffer class for implicit RecordField usage 42 | * 43 | * Internal use only 44 | */ 45 | class RecordSubRangeAccess[T <: Record](b: T) { 46 | def apply(high: Int, low: Int): SelectedRecordFields[T] = { 47 | // here comes the funny part 48 | // elements are listed starting with last defined 49 | // the last element defined are also the lowest ranking bits, starting as index 0 50 | // NB: getWidth must work for this conversion to success 51 | var startingIndex = 0 52 | var index = 0 53 | val fields = b.elements.toSeq.map { case (s, data) => { 54 | val low = startingIndex 55 | val w = data.getWidth 56 | val high = low + w - 1 57 | startingIndex = high + 1 58 | index = index + 1 59 | RecordField(b, s, data, index-1, DRange(high, low), DRange(high, low)) 60 | }} 61 | 62 | // val msg = s"Extract field #${b.index} `${b.name}` with downto range [$high:$low] --> ${b.data}" 63 | 64 | 65 | val sel = fields.flatMap(f => { 66 | if(f.rdef.high <= high && f.rdef.low >= low) { // case of inclusion either exact or with order fields on sides 67 | Some(f) 68 | } else if (f.rdef.high >= high && f.rdef.low == low) { 69 | // trunking fields on higher bits 70 | Some(f.copy(select = f.select.copy(high = high))) 71 | } else if (f.rdef.high == high && f.rdef.low <= low) { 72 | // trunking lower bits 73 | Some(f.copy(select = f.select.copy(low = low))) 74 | } else if (f.rdef.high > high && f.rdef.low < low) { 75 | Some(f.copy(select = DRange(high, low))) 76 | } else { 77 | None // too high or too low to be included 78 | } 79 | }) 80 | 81 | lazy val details = sel.map(f => 82 | if(f.rdef == f.select) s"${f.name} (${f.data})" else s"${f.name}[${f.select.high}:${f.select.low}] (${f.data})" 83 | ).mkString("\n","\n","\n") 84 | RecordConvertUsageWarning.emit(s"Extracted fields from $b[${high}:${low}]: $details") 85 | 86 | SelectedRecordFields(b, sel, DRange(high, low)) 87 | } 88 | } 89 | 90 | case class DRange(high: Int, low: Int){ 91 | def <<(i:Int): DRange = DRange(high+i, low+i) 92 | def >>(i:Int): DRange = DRange(high-i, low-i) 93 | } 94 | 95 | case class SelectedRecordFields[T <: Record](b: T, fields: Seq[RecordField[T]], select: DRange) { 96 | private def getUInt(d: Data, range: DRange): UInt = { 97 | d match { 98 | case bit: Bits => bit.apply(range.high, range.low).asUInt 99 | case v: Vec[_] => new SubWordable(v).apply(range.high, range.low).asUInt 100 | case r: Record => new RecordSubRangeAccess(r).apply(range.high, range.low).asUInt 101 | case t => throw new Exception(s"BundleConvert: Unable to provide subrange read access to $t") 102 | } 103 | } 104 | 105 | def asUInt: UInt = Cat(fields.map(f => { 106 | if(f.select == f.rdef){ 107 | f.data.asUInt 108 | } else { 109 | getUInt(f.data, f.select) 110 | } 111 | })) 112 | 113 | def :=(data: Data): Unit = { 114 | fields match { 115 | case Seq(f) => f := data 116 | case _ => fields.foreach( f => { 117 | val value = getUInt(data, f.select >> select.low) 118 | if(f.select == f.rdef){ 119 | f := value 120 | } else { 121 | data match { 122 | // NB: UInt are NOT subrange assignable 123 | case v: Vec[_] => new SubWordable(v).apply(f.select.high, f.select.low) := value 124 | case r: Record => new RecordSubRangeAccess(r).apply(f.select.high, f.select.low) := value 125 | case t => throw new Exception(s"BundleConvert: Unable to provide subrange write access to $t") 126 | } 127 | } 128 | }) 129 | } 130 | } 131 | } 132 | 133 | 134 | case class RecordField[T <: Record](b: T, name: String, data: Data, index: Int, rdef: DRange, select: DRange) { 135 | // we need very kind connect for this meta field 136 | def :=(d: Data): Unit = { 137 | val h = select.high 138 | val l = select.low 139 | d.widthOption match { 140 | case Some(i) if (i <= h-l + 1) => data := d.asTypeOf(data) // safe 141 | case Some(i) => 142 | println(s"Warning: losing ${i-(h-l+1)} bits of data from $d in its connection to field $name ($data) of bundle $b") 143 | data := d.asTypeOf(data) // Not safe 144 | case _ => 145 | println(s"Critical: unsafe connection of $d to field $name ($data) from bundle $b") 146 | data := d.asTypeOf(data) 147 | } 148 | 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /helpers/src/main/scala/tools/DeepWhen.scala: -------------------------------------------------------------------------------- 1 | package sv2chisel.helpers.tools 2 | 3 | import firrtl._ 4 | import firrtl.ir._ 5 | import firrtl.annotations._ 6 | 7 | import logger.LazyLogging 8 | 9 | import scala.collection.mutable.{HashMap} 10 | 11 | /** Apply WhenContext to Register instanciated within these contexts 12 | * 13 | * {{{ 14 | * val res = Reg(Bool()) 15 | * when (en) { 16 | * val r = Reg(Bool()) 17 | * r := true.B // get when(en) context thanks to this 18 | * transform res := r // always get when(en) context as res is defined outside of WhenContext 19 | * } .otherwise { 20 | * val rb = Reg(Bool()) 21 | * rb := true.B // get when(~en) context thanks to this transform 22 | * res := rb // always get when(~en) context as res is defined outside of WhenContext 23 | * } 24 | * }}} 25 | */ 26 | class DeepWhen extends Transform with DependencyAPIMigration with LazyLogging { 27 | 28 | override val prerequisites = firrtl.stage.Forms.HighForm 29 | override val dependents = Seq.empty 30 | override def invalidates(a: Transform) = false 31 | 32 | /** Stack intended to store the current WhenContext at the current point */ 33 | class CondStack() { 34 | private var _stack = List.empty[Expression] 35 | 36 | /** Add element on top of the stack */ 37 | def push(e: Expression): Unit = { _stack = e :: _stack } 38 | 39 | /** Remove & return last element above the stack */ 40 | def pop(): Expression = { 41 | _stack match { 42 | case Nil => throw new Error("Unexpected empty stack") 43 | case t :: q => _stack = q; t 44 | } 45 | } 46 | 47 | /** învert the logic expression currently on top of the stack */ 48 | def invert(): Unit = { 49 | push(DoPrim(PrimOps.Not, Seq(pop()), Seq(), Utils.BoolType)) 50 | } 51 | 52 | /** recursive helper for getFullCond function */ 53 | private def getRec(e: Expression, l: List[Expression]): Expression = 54 | l match { 55 | case Nil => e 56 | case t :: q => DoPrim(PrimOps.And, Seq(getRec(t, q), e), Seq(), Utils.BoolType) 57 | } 58 | 59 | /** Retrieve the And-concatened expression corresponding to the whole stack */ 60 | def getFullCond(): Option[Expression] = 61 | _stack match { 62 | case Nil => None 63 | case t :: q => Some(getRec(t, q)) 64 | } 65 | } 66 | 67 | /** Transform entry point 68 | * 69 | * @param cs 70 | * the circuit 71 | * @return 72 | * updated circuit 73 | */ 74 | def execute(cs: CircuitState): CircuitState = { 75 | val circuitTarget = CircuitTarget(cs.circuit.main) 76 | 77 | /** process given module statement after statement */ 78 | def processModule(m: DefModule): DefModule = { 79 | val moduleTarget = circuitTarget.module(m.name) 80 | 81 | val condStack = new CondStack() 82 | val regStore = HashMap[ReferenceTarget, Expression]() 83 | 84 | /** Stack current logic context before processing associated statement */ 85 | def processConditionally(c: Conditionally): Conditionally = { 86 | condStack.push(c.pred) 87 | val conseq = c.conseq.mapStmt(processStatements) 88 | condStack.invert() 89 | val alt = c.alt.mapStmt(processStatements) 90 | condStack.pop() 91 | c.copy(conseq = conseq, alt = alt) 92 | } 93 | 94 | /** Add given register to registerStore if a WhenContext is available on Stack */ 95 | def visitRegister(r: DefRegister): Unit = { 96 | condStack.getFullCond() match { 97 | case Some(e) => regStore += ((moduleTarget.ref(r.name), e)) 98 | case None => 99 | } 100 | } 101 | 102 | /** Retrieve ReferenceTarget from complex assignations (LHS only) */ 103 | def getRef(e: Expression): ReferenceTarget = { 104 | e match { 105 | case w: WRef => moduleTarget.ref(w.name) 106 | case w: WSubField => getRef(w.expr) 107 | case w: WSubIndex => getRef(w.expr) 108 | case _ => throw new Error(s"Unexpected assignation to: ${e.serialize}") 109 | } 110 | } 111 | 112 | /** Wrap the given Connect if its location targets an element of the regStore */ 113 | def processConnect(c: Connect): Statement = { 114 | val ref = getRef(c.loc) 115 | if (regStore.contains(ref)) { 116 | Conditionally(NoInfo, regStore(ref), c, EmptyStmt) 117 | } else { 118 | c 119 | } 120 | } 121 | 122 | /** pick up & process relevant Statements for this transform */ 123 | def processStatements(stmt: Statement): Statement = { 124 | stmt match { 125 | case c: Conditionally => processConditionally(c) 126 | case r: DefRegister => visitRegister(r); r 127 | case c: Connect => processConnect(c) 128 | case _ => 129 | // println(s"${stmt.getClass.getName}: ${stmt}") 130 | stmt.mapStmt(processStatements) 131 | } 132 | } 133 | m.mapStmt(processStatements) 134 | } 135 | cs.copy(circuit = cs.circuit.mapModule(processModule)) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /helpers/src/main/scala/tools/ModulePreset.scala: -------------------------------------------------------------------------------- 1 | package sv2chisel.helpers.tools 2 | 3 | import chisel3.experimental.{RunFirrtlTransform} 4 | 5 | import firrtl._ 6 | import firrtl.ir._ 7 | import firrtl.annotations._ 8 | import firrtl.options.Dependency 9 | 10 | import logger.LazyLogging 11 | 12 | import scala.collection.mutable.{ArrayBuffer, HashSet} 13 | 14 | case class ModulePresetAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] { 15 | override def duplicate(n: ModuleTarget): ModulePresetAnnotation = this.copy(target = n) 16 | } 17 | 18 | case class ModulePresetChiselAnnotation(target: ModuleTarget) extends RunFirrtlTransform { 19 | def transformClass = classOf[ModulePreset] // scalastyle:off public.methods.have.type 20 | def toFirrtl = ModulePresetAnnotation(target) // scalastyle:off public.methods.have.type 21 | } 22 | 23 | /** Convert Top Reset Type of Annotated Modules For each module with expected annotation `ModulePresetAnnotation` 24 | * - convert reset IO of any Type into AsyncResetType 25 | * - add `PresetAnnotation` on this IO 26 | * 27 | * Caveats: 28 | * - Expected result is seen only at the emission : no more reset signal 29 | * - Firrtl emission will only reflect modification of the Reset into AsyncResetType 30 | */ 31 | class ModulePreset extends Transform with DependencyAPIMigration with LazyLogging { 32 | 33 | override val prerequisites = firrtl.stage.Forms.HighForm 34 | override val optionalPrerequisiteOf = Seq(Dependency[ModuleRename]) 35 | override def invalidates(a: firrtl.Transform) = false 36 | 37 | /** Recursively update all annotated modules 38 | * - annotated modules are removed from the moduleSet 39 | * - instance declared inside an annotated modules are added to the moduleSet 40 | * - run again until moduleSet is empty 41 | * 42 | * @param circuit 43 | * the circuit 44 | * @param annotations 45 | * all the annotations 46 | * @return 47 | * updated annotations 48 | */ 49 | def update( 50 | cs: CircuitState, 51 | presetModules: Set[ModuleTarget], 52 | topPresetModules: Set[ModuleTarget] 53 | ): CircuitState = { 54 | 55 | // Annotations to be appended and returned as result of the transform 56 | val annos = ArrayBuffer(cs.annotations.toSeq:_*) 57 | val moduleSet = HashSet(presetModules.toSeq:_*) 58 | val circuitTarget = CircuitTarget(cs.circuit.main) 59 | 60 | /** Update annotated module 61 | * - convert reset into AsyncResetType 62 | * - add preset annotation for top level port only (useless to add for all) 63 | */ 64 | def processModule(m: DefModule): DefModule = { 65 | val moduleTarget = circuitTarget.module(m.name) 66 | 67 | def processPorts(port: Port): Port = { 68 | if (port.name == "reset") { 69 | logger.debug(s"[debug] Update reset of ${m.name}") 70 | val target = moduleTarget.ref(port.name) 71 | if (topPresetModules.contains(moduleTarget)) annos += PresetAnnotation(target) 72 | port.copy(tpe = AsyncResetType) 73 | } else { 74 | port 75 | } 76 | } 77 | 78 | def processStatements(stmt: Statement): Statement = { 79 | stmt match { 80 | case i: WDefInstance => 81 | logger.debug( 82 | s"[debug] Registering instance ${i.name} of ${i.module} for AsyncReset Propagation" 83 | ) 84 | moduleSet += circuitTarget.module(i.module) 85 | i 86 | case _ => stmt.mapStmt(processStatements) 87 | } 88 | } 89 | 90 | if (moduleSet.contains(moduleTarget)) { 91 | moduleSet -= moduleTarget 92 | m.mapPort(processPorts).mapStmt(processStatements) 93 | } else { 94 | m 95 | } 96 | } 97 | // ensure that modules are processed in hierarchy order (avoid multiple runs) 98 | val modules = cs.circuit.modules.reverse.map(processModule) 99 | val circuit = cs.circuit.copy(modules = modules.reverse) 100 | val result = cs.copy(circuit = circuit, annotations = annos.toSeq) 101 | if (moduleSet.isEmpty) { 102 | result 103 | } else if(moduleSet.toSet == presetModules) { 104 | logger.error(s"[fatal] Aborting module preset update! ${presetModules.map(_.prettyPrint()).mkString(",")}") 105 | logger.error(s"[fatal] Expect further errors if the top-level reset cannot be found.") 106 | logger.error(s"[fatal] Did you specify the right ModuleTarget?") 107 | result 108 | } else { 109 | logger.warn("[info] Re-running ModulePreset Propagation") 110 | update(result, moduleSet.toSet, topPresetModules) 111 | } 112 | } 113 | 114 | def execute(state: CircuitState): CircuitState = { 115 | // Collect all user-defined PresetAnnotation 116 | val presets = state.annotations 117 | .collect { case m: ModulePresetAnnotation => m } 118 | .groupBy(_.target) 119 | .keySet 120 | // No ModulePresetAnnotation => no need to walk the IR 121 | if (presets.size == 0) { 122 | state 123 | } else { 124 | update(state, presets, presets) 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /helpers/src/main/scala/tools/ModuleRename.scala: -------------------------------------------------------------------------------- 1 | package sv2chisel.helpers.tools 2 | 3 | import chisel3.experimental.RunFirrtlTransform 4 | 5 | import firrtl._ 6 | import firrtl.renamemap.MutableRenameMap 7 | import firrtl.ir._ 8 | import firrtl.annotations._ 9 | import firrtl.options.Dependency 10 | 11 | import logger.LazyLogging 12 | 13 | /** Common trait for ModuleRename annotations 14 | * 15 | * These annotations shall carry the following informations: 16 | * - suffix to be applied to all module & instances within the circuit 17 | * - prefix to be applied to all submodules (and the top if doPrefixTop) 18 | */ 19 | trait ModuleRenameAnnotation { 20 | def prefix: String 21 | def suffix: String 22 | def doPrefixTop: Boolean 23 | def topOnly: Boolean 24 | 25 | /** Both prefix & suffix are always applied for submodules 26 | * 27 | * Note that "suffix" is applied on prefix to match hierarchical naming with suffixing of top name 28 | */ 29 | final def newSubModuleName(oldName: String): String = s"${prefix}_${suffix}_$oldName" 30 | 31 | /** for top module suffix is always applied but prefix is applied only when doPrefixTop is true */ 32 | final def newTopName(oldName: String) = if (doPrefixTop) s"${prefix}_${oldName}_$suffix" else s"${oldName}_$suffix" 33 | } 34 | 35 | /** ModuleRename firrtl annotation simply implementing the common trait as val of a case class */ 36 | case class ModuleRenameFirrtlAnnotation( 37 | prefix: String, 38 | suffix: String, 39 | doPrefixTop: Boolean, 40 | topOnly: Boolean 41 | ) extends NoTargetAnnotation 42 | with ModuleRenameAnnotation 43 | 44 | /** ModuleRename chisel annotation implemnting the ModuleRenameAnnotation trait 45 | * 46 | * This chisel annotation has multiple roles: 47 | * - it records prefix and suffix to be applied to all module & instances within the circuit 48 | * - it adds the transform `ModuleRename` to the firrtl compilation flow 49 | * - it is converted into the firrtl's ModuleRenameAnnotation 50 | */ 51 | case class ModuleRenameChiselAnnotation( 52 | prefix: String, 53 | suffix: String, 54 | doPrefixTop: Boolean = false, 55 | topOnly: Boolean = false 56 | ) extends RunFirrtlTransform 57 | with ModuleRenameAnnotation { 58 | def transformClass = classOf[ModuleRename] 59 | def toFirrtl = ModuleRenameFirrtlAnnotation(prefix, suffix, doPrefixTop, topOnly) 60 | } 61 | 62 | /** Transform adding a suffix to all module/instances of the circuit 63 | * 64 | * The suffix applies globally (without any filter) and is provided by the firrtl's `ModuleRenameAnnotation` 65 | */ 66 | class ModuleRename extends Transform with DependencyAPIMigration with LazyLogging { 67 | // Wiring is not a prerequisite but when mixed-in (by BoringUtils), it must be run before the renaming 68 | override val prerequisites = firrtl.stage.Forms.LowForm 69 | override val optionalPrerequisites = Seq(Dependency[firrtl.passes.wiring.WiringTransform]) 70 | override def invalidates(a: Transform) = false 71 | 72 | /** Actual IR-modifying function, renaming all modules & instances 73 | * 74 | * @param circuit 75 | * the circuit 76 | * @param anno 77 | * ModuleRenameAnnotation providing the renaming functions 78 | * @return 79 | * updated circuit with the associated RenameMap 80 | */ 81 | def update(circuit: Circuit, anno: ModuleRenameAnnotation): (Circuit, RenameMap) = { 82 | // This RenameMap is required such that the firrtl compiler is able to track external references to elements of this 83 | // circuit which includes (most notably) TargetAnnotations, such as the ones used for preset annotation 84 | // Not providing this RenameMap leads to immediate crash in the following transforms (where last is the emitter) 85 | val renames = MutableRenameMap() 86 | 87 | val prevTarget = CircuitTarget(circuit.main) 88 | 89 | // circuit main must be renamed as well to match the actual name of top module 90 | val newMain = anno.newTopName(circuit.main) 91 | val newTarget = CircuitTarget(newMain) 92 | renames.record(prevTarget, newTarget) 93 | 94 | /** Update module instances references, still required despite the rename map 95 | * 96 | * Renaming both blackboxes and vanilla instances because it would be harder to filter blackbox out. This is an 97 | * internal reference name only and does not impact the actual user "desiredName" actually emitted. 98 | */ 99 | def processStatements(stmt: Statement): Statement = { 100 | stmt match { 101 | case i: DefInstance if(!anno.topOnly) => 102 | val newRef = anno.newSubModuleName(i.module) // there cannot be any instances of main 103 | logger.debug(s"[debug] Updating reference for instance ${i.name} from ${i.module} to ${newRef}") 104 | i.copy(module = newRef) 105 | case _ => stmt.mapStmt(processStatements) 106 | } 107 | } 108 | 109 | /** Update module name, look for instances within modules' statements */ 110 | def processModule(d: DefModule): DefModule = { 111 | val newName = if (d.name == circuit.main) newMain else anno.newSubModuleName(d.name) 112 | d.mapStmt(processStatements) match { 113 | case m: Module if(!anno.topOnly || d.name == circuit.main) => 114 | logger.debug(s"[debug] Renaming module ${d.name} into ${newName}") 115 | renames.record(prevTarget.module(d.name), newTarget.module(newName)) 116 | m.copy(name = newName) 117 | case m: ExtModule if(!anno.topOnly) => 118 | // this rename does not override the desiredName (m.defname) 119 | logger.debug(s"[debug] Renaming external module (blackbox) ${d.name} into ${newName}") 120 | renames.record(prevTarget.module(d.name), newTarget.module(newName)) 121 | m.copy(name = newName) 122 | case _ => d 123 | } 124 | } 125 | 126 | // modules are naturally processed in reverse hierarchical order 127 | // here we have no dependency requirement thanks to the static suffix 128 | // Do not forget to rename the main module (pointer to the top module) 129 | (circuit.copy(main = newMain, modules = circuit.modules.map(processModule)), renames) 130 | } 131 | 132 | /** Main method: entry point for the transform 133 | * - triggers only if a ModuleRenameAnnotation is found 134 | * - if multiple ModuleRenameAnnotation are found, only the first one is considered 135 | */ 136 | def execute(state: CircuitState): CircuitState = { 137 | state.annotations.collectFirst { case a: ModuleRenameFirrtlAnnotation => a } match { 138 | case Some(anno) => 139 | val (circuit, renames) = update(state.circuit, anno) 140 | state.copy(circuit = circuit, renames = Some(renames)) 141 | 142 | case _ => state // No ModuleRenameFirrtlAnnotation => no need to walk the IR 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /helpers/src/main/scala/vecconvert.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel.helpers 6 | 7 | import chisel3._ 8 | 9 | /** Package holding all implicit conversion for subword assignments 10 | * 11 | * This package must be imported if subwords assignments are necessary. It allows a number of operation, through 12 | * implicit conversion, in order to ease UInt usage as bit vectors. 13 | * 14 | * In all vecconvert package, we use Chisel3 convention: (high, low) slicing, high is included. 15 | * 16 | */ 17 | package object vecconvert { 18 | implicit def vecToSubwords[T <: Data](v: Vec[T]): SubWordable[T] = new SubWordable(v) 19 | implicit def subwordsToVec[T <: Data](s: SubWords[T]): Vec[T] = s.asVec 20 | implicit def dataToBitPatternConnectable[T <: Data](d: T): BitPatternConnectable[T] = 21 | new BitPatternConnectable(d) 22 | implicit def stringToHwString(s: String): HwString = new HwString(s) 23 | } 24 | 25 | /** Vec-based representation of a String in Hardware (using ASCII codes for characters) 26 | * 27 | * This can be used to initialize some Vectors for example: 28 | * 29 | * {{{ 30 | * val s = Wire(Vec(12, UInt(8.W))) 31 | * s := "Hello World".V 32 | * s := "World".V(12.W) 33 | * }}} 34 | */ 35 | class HwString(s: String) { 36 | 37 | /** Simple conversion (length is inferred based on string length) */ 38 | def V: Vec[UInt] = { //scalastyle:ignore method.name 39 | VecInit(s.toCharArray.toIndexedSeq.map(_.U(8.W))) 40 | } 41 | 42 | /** Conversion with length specified 43 | * 44 | * The conversion will fail if the String is too long to be stored in with the specified length. It the String is 45 | * shorter, it will be 0-padded (on right-aligned, so left-padded). 46 | * 47 | * @param w 48 | * required width for the final Vec 49 | */ 50 | def V(w: internal.firrtl.Width): Vec[UInt] = { //scalastyle:ignore method.name 51 | val n = w.get 52 | val chars = s.toCharArray 53 | val l = chars.length 54 | require(l <= n, s"String `$s' is too long (${l}) to fit in expected Vec($n, UInt(8.W))") 55 | /* If the size of a string assigned to a string variable is smaller than the declared size of the variable, then it 56 | * will be left-padded with zeros. */ 57 | // from renerta http://verilog.renerta.com/source/vrg00048.htm 58 | VecInit.tabulate(n)(i => if (i < (n - l)) 0.U(8.W) else chars(i - n + l).U(8.W)) 59 | } 60 | } 61 | 62 | /** Representation of Vec subwords 63 | * 64 | * Vanilla Chisel3 does not allow slicing a Vec (you can only select a single element of the Vec). This class fills 65 | * this gap, by representing a sub-vector which can then be cast to a Vec (and thus to other elements). This can be 66 | * used directly, but is managed by the implicit conversion of vecconvert package. This class respects the Chisel3 67 | * convention for slices: high (included) downto low (which is not the same as Scala convention) 68 | * 69 | * It can be used directly or implicitly : 70 | * {{{ 71 | * val baseVector = VecInit(Seq(true.B, false.B, true.B)) 72 | * // Direct usage 73 | * val directSlice = SubWords(baseVector, 1, 0).asVec 74 | * // Implicit usage 75 | * import vecconvert._ 76 | * val implSlice = baseVector(1, 0) 77 | * }}} 78 | * 79 | * @tparam T 80 | * contained type of the vector we want to slice 81 | * @param v 82 | * vector we want to slice 83 | * @param high 84 | * high index of the slice (included) 85 | * @param low 86 | * low index of the slice (included) 87 | */ 88 | case class SubWords[T <: Data](v: Vec[T], high: Int, low: Int) { 89 | require(high >= low, s"Subrange are expected with downto ranges: from high to low. Got high:$high & low:$low") 90 | val length = high - low + 1 91 | 92 | // Seq where first element is LSB (as everywhere else in chisel) 93 | def :=(s: Seq[T]): Unit = do_connect(s) 94 | 95 | private def do_connect(s: Seq[Data]): Unit = { 96 | require( 97 | s.length <= length, 98 | s"Too many elements given ($s) for connection with ${v.slice(low, high + 1)}" 99 | ) 100 | require( 101 | s.length >= length, 102 | s"Too few elements given ($s) for connection with ${v.slice(low, high + 1)}" 103 | ) 104 | s.zip(v.slice(low, high + 1)).foreach(t => t._2 := t._1) 105 | } 106 | 107 | def :=(v: Vec[T]): Unit = do_connect(v.toSeq) 108 | def :=(u: UInt): Unit = { 109 | v.headOption match { 110 | case Some(_:Bool) => do_connect(u.asBools) 111 | case Some(t) => throw new Exception(s"TODO: Split UInt as bools and group by t.widthOption elements (${t})") 112 | case _ => // nothing to do: empty vec 113 | } 114 | 115 | } 116 | // asUInt 117 | // Aggregates are recursively packed with the first element appearing in the least-significant bits of the result. 118 | def asVec: Vec[T] = VecInit(v.slice(low, high + 1)) 119 | def asUInt: UInt = asVec.asUInt 120 | def asTypeOf[S <: Data](data: S): S = asVec.asTypeOf(data) 121 | def U: UInt = asUInt //scalastyle:ignore method.name 122 | 123 | } 124 | 125 | /** Buffer class for implicit Subwords usage 126 | * 127 | * Internal use only 128 | */ 129 | class SubWordable[T <: Data](v: Vec[T]) { 130 | def apply(high: Int, low: Int): SubWords[T] = SubWords(v, high, low) 131 | } 132 | 133 | /** Trait used to define usual bit patterns */ 134 | sealed trait BitPattern 135 | 136 | /** All zeroes bit pattern */ 137 | case object Zeroes extends BitPattern 138 | 139 | /** All ones bit pattern */ 140 | case object Ones extends BitPattern 141 | 142 | /** Simplified assignment of usual bit patterns 143 | * 144 | * This can be used through implicit usage: 145 | * {{{ 146 | * val t = Wire(UInt(8.W)) 147 | * t := Ones // => Will assign all 8 bits to 1 148 | * }}} 149 | */ 150 | class BitPatternConnectable[T <: Data](d: Data) { 151 | def :=(that: BitPattern): Unit = { 152 | that match { 153 | case Zeroes => d := 0.U.asTypeOf(d) 154 | case Ones => d := (~0.U(d.getWidth.W)).asTypeOf(d) 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /helpers/src/test/scala/HwEnumSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselHelpersTests 6 | 7 | import sv2chiselHelpersTests.utils._ 8 | import sv2chisel.helpers.enum._ 9 | 10 | import chisel3._ 11 | import chisel3.stage.ChiselStage 12 | 13 | import org.scalatest._ 14 | import flatspec._ 15 | 16 | class HwEnumSpec extends AnyFlatSpec with VerilogMatchers { 17 | val setTestRunDir = Array("--target-dir", "test_run_dir") 18 | 19 | "GenericHwEnum" should "just works" in { 20 | 21 | // warning: this type aliasing might hide bugs: confusing literal value (MyEnum.Value) with hardware type (UInt) 22 | // type MyEnum = UInt // GenericHwEnum are simple UInt but provide syntax convenience 23 | object MyEnum extends GenericHwEnum { 24 | val stateA = Value 25 | val stateB = Value 26 | val stateC = Value 27 | } 28 | 29 | class Example extends Module { 30 | val in = IO(Input(Bool())) 31 | val out = IO(Output(MyEnum())) 32 | 33 | val test = Wire(MyEnum()) 34 | when(in){ 35 | test := MyEnum.stateA 36 | } .otherwise { 37 | test := (MyEnum.maxId - 1).U // illustrate scala enumeration features 38 | } 39 | 40 | // typical reason why providing MyEnum aliasing is not a good idea 41 | // def fun(en: MyEnum): MyEnum = en match { 42 | // case MyEnum.stateA => MyEnum.stateB // fruitless test : UInt against UInt literal => but compiler cannot warn 43 | // case MyEnum.stateB => MyEnum.stateA 44 | // case _ => println("always true"); en 45 | // } 46 | // software: dealing with scala literal values 47 | def funSw(en: MyEnum.Value): MyEnum.Value = en match { 48 | case MyEnum.stateA => MyEnum.stateB // match scala literal values for equivalence 49 | case MyEnum.stateB => MyEnum.stateA 50 | case _ => en 51 | } 52 | // hardware: dealing with signal and hardware literal 53 | def funHw(en: UInt): UInt = { 54 | val res = Wire(en.cloneType) 55 | when(en === MyEnum.stateA){ // implicit conversion to UInt => enabling to apply the equality in hardware 56 | res := MyEnum.stateB // implicit conversion to UInt 57 | } .elsewhen(en === MyEnum.stateB) { 58 | res := MyEnum.stateA 59 | } .otherwise { 60 | res := en 61 | } 62 | res 63 | } 64 | 65 | // when(fun(test) === fun(MyEnum.stateB)) { //quite risky for fun(test), basically doing nothing 66 | // NB: using suggestName to get predictible name for intermediate verilog wires 67 | when((funHw(test).suggestName("funHw") === funSw(MyEnum.stateB)).suggestName("bool")) { 68 | out := test 69 | } .otherwise { 70 | out := 0.U 71 | } 72 | } 73 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 74 | verilog should containStr ("wire [1:0] test = in ? 2'h0 : 2'h2;") 75 | verilog should containStr ("wire bool = funHw == 2'h0;") 76 | verilog should containStr ("assign out = bool ? test : 2'h0;") 77 | 78 | } 79 | 80 | "CustomHwEnum" should "just works" in { 81 | 82 | // warning: this type aliasing might hide bugs: confusing literal value (MyEnum.Value) with hardware type (UInt) 83 | // type MyEnum = UInt // GenericHwEnum are simple UInt but provide syntax convenience 84 | object MyEnum extends CustomHwEnum { 85 | val stateA = V(0.U) 86 | val stateB = V(12.U) 87 | val stateC = V(5.U) 88 | } 89 | 90 | class Example extends Module { 91 | val in = IO(Input(Bool())) 92 | val out = IO(Output(MyEnum())) 93 | 94 | val test = Wire(MyEnum()) 95 | when(in){ 96 | test := MyEnum.stateA 97 | } .otherwise { 98 | test := MyEnum.stateB 99 | } 100 | 101 | // typical reason why providing MyEnum aliasing is not a good idea 102 | // def fun(en: MyEnum): MyEnum = en match { 103 | // case MyEnum.stateA => MyEnum.stateB // fruitless test : UInt against UInt literal => but compiler cannot warn 104 | // case MyEnum.stateB => MyEnum.stateA 105 | // case _ => println("always true"); en 106 | // } 107 | // software: dealing with scala literal values 108 | def funSw(en: MyEnum.Value): MyEnum.Value = en match { 109 | case MyEnum.stateA => MyEnum.stateB // match scala literal values for equivalence 110 | case MyEnum.stateB => MyEnum.stateA 111 | case _ => en 112 | } 113 | // hardware: dealing with signal and hardware literal 114 | def funHw(en: UInt): UInt = { 115 | val res = Wire(en.cloneType) 116 | when(en === MyEnum.stateA){ // implicit conversion to UInt => enabling to apply the equality in hardware 117 | res := MyEnum.stateB // implicit conversion to UInt 118 | } .elsewhen(en === MyEnum.stateB) { 119 | res := MyEnum.stateA 120 | } .otherwise { 121 | res := en 122 | } 123 | res 124 | } 125 | 126 | // when(fun(test) === fun(MyEnum.stateB)) { //quite risky for fun(test), basically doing nothing 127 | // NB: using suggestName to get predictible name for intermediate verilog wires 128 | when((funHw(test).suggestName("funHw") === funSw(MyEnum.stateA)).suggestName("bool")) { 129 | out := test 130 | } .otherwise { 131 | out := 0.U 132 | } 133 | } 134 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 135 | verilog should containStr ("wire [3:0] test = in ? 4'h0 : 4'hc;") 136 | verilog should containStr ("wire bool = funHw == 4'hc;") 137 | verilog should containStr ("assign out = bool ? test : 4'h0;") 138 | 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /helpers/src/test/scala/MemInitSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselHelpersTests 6 | 7 | import sv2chiselHelpersTests.utils._ 8 | import sv2chisel.helpers.MemInit 9 | 10 | import chisel3._ 11 | import chisel3.stage.ChiselStage 12 | 13 | import org.scalatest._ 14 | import flatspec._ 15 | 16 | class MemInitSpec extends AnyFlatSpec with VerilogMatchers { 17 | val setTestRunDir = Array("--target-dir", "test_run_dir") 18 | 19 | behavior of "MemInit" 20 | 21 | def checkNoReg(verilog: String): Unit = { 22 | verilog should containStr("reg [4:0] rom [0:3];") 23 | verilog should containStr("assign rom_out_MPORT_data = rom[rom_out_MPORT_addr];") 24 | verilog should containStr("assign out = rom_out_MPORT_data;") 25 | verilog should containStr ( 26 | "rom[0] = 0;", 27 | "rom[1] = 1;", 28 | "rom[2] = 3;", 29 | "rom[3] = 7;", 30 | "end" 31 | ) 32 | } 33 | 34 | 35 | it should "work with VecInit style apply" in { 36 | 37 | class Example() extends Module { 38 | val sel = IO(Input(UInt(2.W))) 39 | val out = IO(Output(UInt(5.W))) 40 | val rom = MemInit(0.U(5.W), 1.U(5.W), 3.U(5.W), 7.U(5.W)) 41 | out := rom(sel) 42 | } 43 | 44 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 45 | checkNoReg(verilog) 46 | } 47 | 48 | it should "work with VecInit style apply (reg)" in { 49 | 50 | class Example() extends Module { 51 | val sel = IO(Input(UInt(2.W))) 52 | val en = IO(Input(Bool())) 53 | val out = IO(Output(UInt(5.W))) 54 | val outR = Reg(UInt(5.W)) 55 | val rom = MemInit(0.U(5.W), 1.U(5.W), 3.U(5.W), 7.U(5.W)) 56 | outR := rom(sel) 57 | out := outR 58 | } 59 | 60 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 61 | verilog should containStr("reg [4:0] rom [0:3];") 62 | verilog should containStr("assign out = outR;") 63 | verilog should containStr("outR <= rom_outR_MPORT_data;") 64 | verilog should containStr ( 65 | "rom[0] = 0;", 66 | "rom[1] = 1;", 67 | "rom[2] = 3;", 68 | "rom[3] = 7;", 69 | "end" 70 | ) 71 | } 72 | 73 | it should "work with VecInit style apply (seq)" in { 74 | 75 | class Example() extends Module { 76 | val sel = IO(Input(UInt(2.W))) 77 | val out = IO(Output(UInt(5.W))) 78 | val rom = MemInit(Seq(0.U(5.W), 1.U(5.W), 3.U(5.W), 7.U(5.W))) 79 | out := rom(sel) 80 | } 81 | 82 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 83 | checkNoReg(verilog) 84 | } 85 | 86 | it should "work with VecInit style apply (Vec.Lit)" in { 87 | // hang in scala 2.13... ? 88 | class Example() extends Module { 89 | val sel = IO(Input(UInt(2.W))) 90 | val out = IO(Output(UInt(5.W))) 91 | import chisel3.experimental.VecLiterals._ 92 | val rom = MemInit(Vec.Lit(0.U(5.W), 1.U(5.W), 3.U(5.W), 7.U(5.W))) 93 | out := rom(sel) 94 | } 95 | 96 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 97 | checkNoReg(verilog) 98 | } 99 | 100 | it should "work with basic apply" in { 101 | 102 | class Example() extends Module { 103 | val sel = IO(Input(UInt(2.W))) 104 | val out = IO(Output(UInt(5.W))) 105 | 106 | val rom = MemInit(4, UInt(5.W), Seq(0,1,3,7).map(BigInt(_))) 107 | out := rom(sel) 108 | } 109 | 110 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 111 | checkNoReg(verilog) 112 | } 113 | 114 | it should "work with fill" in { 115 | 116 | class Example() extends Module { 117 | val sel = IO(Input(UInt(2.W))) 118 | val out = IO(Output(UInt(5.W))) 119 | 120 | val rom = MemInit.fill(4, UInt(5.W))(BigInt(0)) 121 | out := rom(sel) 122 | } 123 | 124 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 125 | verilog should containStr("reg [4:0] rom [0:3];") 126 | verilog should containStr("assign rom_out_MPORT_data = rom[rom_out_MPORT_addr];") 127 | verilog should containStr("assign out = rom_out_MPORT_data;") 128 | verilog should containStr ( 129 | "for (initvar = 0; initvar < 4; initvar = initvar+1)", 130 | "rom[initvar] = 0;" 131 | ) 132 | } 133 | 134 | it should "work with tabulate" in { 135 | 136 | class Example() extends Module { 137 | val sel = IO(Input(UInt(2.W))) 138 | val out = IO(Output(UInt(5.W))) 139 | 140 | val rom = MemInit.tabulate(4, UInt(5.W))(i => (BigInt(1) << i) - 1) 141 | out := rom(sel) 142 | } 143 | 144 | val verilog = (new ChiselStage()).emitVerilog(new Example(), setTestRunDir) 145 | checkNoReg(verilog) 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /helpers/src/test/scala/bundleconvertSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselHelpersTests 6 | 7 | import sv2chiselHelpersTests.utils._ 8 | import sv2chisel.helpers.bundleconvert._ 9 | 10 | import chisel3._ 11 | import chisel3.stage.ChiselStage 12 | 13 | import org.scalatest._ 14 | import flatspec._ 15 | 16 | class bundleconvertSpec extends AnyFlatSpec with VerilogMatchers { 17 | val setTestRunDir = Array("--target-dir", "test_run_dir") 18 | 19 | behavior of "BundleSubRangeAccess" 20 | 21 | it should "allow field extraction by range" in { 22 | class SubRangeBundle() extends RawModule { 23 | class MyBundle extends Bundle { 24 | val a = Bool() // index (7, 7) 25 | val b = Bool() // index (6, 6) 26 | val c = UInt(5.W) // index (5, 1) 27 | val d = Bool() // index (0,0) 28 | } 29 | val out = IO(Output(new MyBundle)) 30 | out(0,0) := true.B 31 | out(5,1) := 4.U 32 | out(6,6) := true.B 33 | out(7,7) := false.B 34 | } 35 | val verilog = (new ChiselStage()).emitVerilog(new SubRangeBundle(), setTestRunDir) 36 | 37 | verilog should containStr ("assign out_a = 1'h0;") 38 | verilog should containStr ("assign out_b = 1'h1;") 39 | verilog should containStr ("assign out_c = 5'h4;") 40 | verilog should containStr ("assign out_d = 1'h1;") 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /helpers/src/test/scala/tools/ChiselGenSpec.scala: -------------------------------------------------------------------------------- 1 | package sv2chiselHelpersTests.tools 2 | 3 | import sv2chisel.helpers.tools._ 4 | import sv2chiselHelpersTests.utils._ 5 | 6 | import chisel3._ 7 | 8 | import org.scalatest._ 9 | import flatspec._ 10 | 11 | class ChiselGenSpec extends AnyFlatSpec with VerilogMatchers { 12 | class SimpleRegInit() extends Module { 13 | val in = IO(Input(Bool())) 14 | val out = IO(Output(Bool())) 15 | val r = RegInit(false.B) 16 | r := in 17 | out := r 18 | } 19 | 20 | behavior of "ChiselGen" 21 | 22 | val setTestRunDir = Array( 23 | "--target-dir", 24 | "test_run_dir" 25 | ) 26 | 27 | it should "just works with syncronous Reset" in { 28 | val verilog = ChiselGen.emit(new SimpleRegInit(), setTestRunDir) 29 | verilog should containStr( 30 | "always @(posedge clock) begin", 31 | "if (reset) begin", 32 | "r <= 1'h0;", 33 | "end else begin", 34 | "r <= in;", 35 | "end", 36 | "end" 37 | ) 38 | } 39 | 40 | it should "just works with Preset" in { 41 | val verilog = ChiselGen.emitPreset(new SimpleRegInit(), setTestRunDir) 42 | verilog should containStr("reg r = 1'h0;") 43 | verilog should containStr( 44 | "always @(posedge clock) begin", 45 | "r <= in;", 46 | "end" 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /helpers/src/test/scala/tools/DeepWhenSpec.scala: -------------------------------------------------------------------------------- 1 | package sv2chiselHelpersTests.tools 2 | 3 | import sv2chisel.helpers.tools._ 4 | import sv2chiselHelpersTests.utils._ 5 | 6 | import chisel3._ 7 | import chisel3.stage.ChiselStage 8 | 9 | import org.scalatest._ 10 | import flatspec._ 11 | 12 | class DeepWhenSpec extends AnyFlatSpec with VerilogMatchers { 13 | val setTestRunDir = Array("--target-dir", "test_run_dir") 14 | 15 | def getProcessedVerilog(m: => RawModule): String = { 16 | val annos = Seq(firrtl.stage.RunFirrtlTransformAnnotation(new DeepWhen())) 17 | (new ChiselStage()).emitVerilog(m, setTestRunDir, annos) 18 | } 19 | 20 | class BasicWhenInner() extends Module { 21 | val en = IO(Input(Bool())) 22 | val in = IO(Input(UInt(3.W))) 23 | val out = IO(Output(UInt(3.W))) 24 | 25 | val rout = Reg(chiselTypeOf(out)) 26 | 27 | when(en) { 28 | val innerA = Reg(in.cloneType) 29 | innerA := in 30 | rout := innerA 31 | }.otherwise { 32 | val innerB = Reg(in.cloneType) 33 | innerB := in 34 | rout := innerB 35 | } 36 | out := rout 37 | } 38 | 39 | behavior of "DeepWhenTransform" 40 | 41 | it should "be useful" in { 42 | // check if it has been patched upstream 43 | val raw = ChiselStage.emitVerilog(new BasicWhenInner()) 44 | val processed = getProcessedVerilog(new BasicWhenInner()) 45 | 46 | raw should containStr("innerA <= in;", "innerB <= in;") 47 | processed shouldNot contain("innerA <= in;", "innerB <= in;") 48 | } 49 | it should "work with basic assignations" in { 50 | val processed = getProcessedVerilog(new BasicWhenInner()) 51 | 52 | processed should containStr("if (en) begin", "innerA <= in;", "end") 53 | processed should containStr("if (~en) begin", "innerB <= in;", "end") 54 | } 55 | 56 | class MoreAdvancedInner() extends Module { 57 | val en = IO(Input(Bool())) 58 | val in = IO(Input(UInt(3.W))) 59 | val out = IO(Output(UInt(9.W))) 60 | 61 | val rout = Reg(chiselTypeOf(out)) 62 | 63 | when(en) { 64 | val inner1 = Reg(new Bundle { 65 | val test = in.cloneType 66 | val testV = Vec(2, in.cloneType) 67 | }) 68 | inner1.test := in 69 | inner1.testV(0) := in 70 | inner1.testV(1) := in 71 | rout := inner1.asUInt 72 | } 73 | out := rout 74 | } 75 | 76 | it should "work with advanced assignations" in { 77 | val processed = getProcessedVerilog(new MoreAdvancedInner()) 78 | processed should containStr("if (en) begin", "inner1_test <= in;", "end") 79 | processed should containStr("if (en) begin", "inner1_testV_0 <= in;", "end") 80 | processed should containStr("if (en) begin", "inner1_testV_1 <= in;", "end") 81 | } 82 | 83 | class InnerInner() extends Module { 84 | val cond = IO(Input(Vec(2, Bool()))) 85 | val in = IO(Input(UInt(3.W))) 86 | val out = IO(Output(UInt(3.W))) 87 | 88 | val rout = Reg(chiselTypeOf(out)) 89 | 90 | when(cond(0)) { 91 | val inner1 = Reg(in.cloneType) 92 | inner1 := in 93 | when(cond(1)) { 94 | val inner2 = Reg(in.cloneType) 95 | inner2 := inner1 96 | rout := inner2 97 | } 98 | } 99 | out := rout 100 | } 101 | 102 | it should "work with inner inner assignations" in { 103 | val processed = getProcessedVerilog(new InnerInner()) 104 | 105 | processed should containStr("if (cond_0 & cond_1) begin", "inner2 <= inner1;", "end") 106 | processed should containStr("if (cond_0) begin", "inner1 <= in;", "end") 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /helpers/src/test/scala/utils/VerilogMatchers.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselHelpersTests.utils 6 | 7 | import org.scalatest._ 8 | import matchers.should._ 9 | 10 | import collection.mutable.ArrayBuffer 11 | 12 | trait VerilogMatchers extends Matchers { 13 | import matchers._ 14 | 15 | /** Checks that the emitted circuit has the expected lines in order */ 16 | def containExactly(expectedLines: String*) = new VerilogStrictStringsMatcher(expectedLines) 17 | def containStr(expectedLines: String*) = new VerilogFlexStringsMatcher(expectedLines) 18 | 19 | def findFaillingLine(data: Seq[String], expected: Seq[String]): String = { 20 | val msg = ArrayBuffer[String]() 21 | val starts = data.zipWithIndex.collect { case (s, t) if (s == expected.head) => t } 22 | if(starts.isEmpty) msg += s"[DEBUG] Unable to find a first matching line (${expected.head})" 23 | starts.foreach(i => { 24 | msg += s"DEBUG: starting at index $i" 25 | data.drop(i).zip(expected).foreach { 26 | case (chisel, exp) if (chisel != exp ) => msg += s"[DEBUG]: failing at `$chisel` (expected: `$exp`)" 27 | case _ => 28 | } 29 | }) 30 | msg.mkString("\n") 31 | } 32 | 33 | class VerilogStrictStringsMatcher(expectedLines: Seq[String]) extends Matcher[String] { 34 | override def apply(verilog: String): MatchResult = { 35 | val data = verilog.split("\n").toSeq 36 | MatchResult( 37 | data.containsSlice(expectedLines), 38 | verilog + "\n did not contain \"" + expectedLines + "\"\n" +"\nDetails:\n" + findFaillingLine(data, expectedLines), 39 | s"Emitted verilog contained $expectedLines" 40 | ) 41 | } 42 | } 43 | class VerilogFlexStringsMatcher(expectedLines: Seq[String]) extends Matcher[String] { 44 | override def apply(verilog: String): MatchResult = { 45 | val data = stripComments(verilog).split("\n").map(_.trim).toSeq 46 | MatchResult( 47 | data.containsSlice(expectedLines), 48 | verilog + "\n did not contain \"" + expectedLines + "\"\n" +"\nDetails:\n" + findFaillingLine(data, expectedLines), 49 | s"Emitted verilog contained $expectedLines" 50 | ) 51 | } 52 | } 53 | def stripComments(str: String): String = { 54 | "//.*\n".r.replaceAllIn(str, "\n") 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /helpers/src/test/scala/vecconvertSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselHelpersTests 6 | 7 | import sv2chiselHelpersTests.utils._ 8 | import sv2chisel.helpers._ 9 | import sv2chisel.helpers.vecconvert._ 10 | 11 | import chisel3._ 12 | import chisel3.stage.ChiselStage 13 | 14 | import org.scalatest._ 15 | import flatspec._ 16 | 17 | class vecconvertSpec extends AnyFlatSpec with VerilogMatchers { 18 | val setTestRunDir = Array("--target-dir", "test_run_dir") 19 | 20 | "Ascii literals" should "just works" in { 21 | class VecInitTest() extends RawModule { 22 | val outA = IO(Output(Vec(8, UInt(8.W)))) 23 | val outB = IO(Output(Vec(8, UInt(8.W)))) 24 | val outC = IO(Output(UInt(64.W))) 25 | 26 | outA := "testtest".V 27 | outB := "test".V(8.W) 28 | outC := "test".V.asUInt 29 | } 30 | val verilog = (new ChiselStage()).emitVerilog(new VecInitTest(), setTestRunDir) 31 | verilog should containStr ("assign outA_0 = 8'h74;") 32 | verilog should containStr ("assign outA_1 = 8'h65;") 33 | verilog should containStr ("assign outA_2 = 8'h73;") 34 | verilog should containStr ("assign outA_3 = 8'h74;") 35 | verilog should containStr ("assign outA_4 = 8'h74;") 36 | verilog should containStr ("assign outA_5 = 8'h65;") 37 | verilog should containStr ("assign outA_6 = 8'h73;") 38 | verilog should containStr ("assign outA_7 = 8'h74;") 39 | 40 | verilog should containStr ("assign outB_0 = 8'h0;") 41 | verilog should containStr ("assign outB_1 = 8'h0;") 42 | verilog should containStr ("assign outB_2 = 8'h0;") 43 | verilog should containStr ("assign outB_3 = 8'h0;") 44 | verilog should containStr ("assign outB_4 = 8'h74;") 45 | verilog should containStr ("assign outB_5 = 8'h65;") 46 | verilog should containStr ("assign outB_6 = 8'h73;") 47 | verilog should containStr ("assign outB_7 = 8'h74;") 48 | 49 | verilog should containStr ("assign outC = 64'h74736574;") // NB: reversed as expected 50 | } 51 | 52 | "Implicit subwords" should "enable direct subrange assignments" in { 53 | class TestVecSubWords() extends RawModule { 54 | val outB = IO(Output(Vec(5, Bool()))) 55 | // enabled by default 56 | outB(0) := false.B 57 | // possible thanks to new implicits 58 | outB(2, 1) := VecInit(true.B, true.B) 59 | outB(4, 3) := Seq(false.B, false.B) 60 | } 61 | val verilog = (new ChiselStage()).emitVerilog(new TestVecSubWords(), setTestRunDir) 62 | verilog should containStr ("assign outB_0 = 1'h0;") 63 | verilog should containStr ("assign outB_1 = 1'h1;") 64 | verilog should containStr ("assign outB_2 = 1'h1;") 65 | verilog should containStr ("assign outB_3 = 1'h0;") 66 | verilog should containStr ("assign outB_4 = 1'h0;") 67 | } 68 | it should "enable direct subrange slicing and use in arithmetic expression" in { 69 | class TestVecSubWords() extends RawModule { 70 | val in = IO(Input(Vec(5, Bool()))) 71 | val outA = IO(Output(UInt(2.W))) 72 | val outB = IO(Output(UInt(3.W))) 73 | val outC = IO(Output(Bool())) 74 | 75 | outA := in(2, 1).asUInt 76 | outB := 3.U + in(4, 3).asUInt 77 | outC := 1.U === in(4, 3).asUInt 78 | 79 | // let's do some checks about MSB // Vec2Int conversions 80 | val checkA = IO(Output(Bool())) 81 | val checkB = IO(Output(Bool())) 82 | val checkC = IO(Output(Bool())) 83 | checkA := VecInit(Seq(false.B, false.B, true.B)).asUInt === 4.U 84 | checkB := (4.U)(1, 0) === 0.U 85 | val bools = (4.U).asBools 86 | checkC := bools(0) === false.B && bools(1) === false.B && bools(2) === true.B 87 | 88 | } 89 | val verilog = (new ChiselStage()).emitVerilog(new TestVecSubWords(), setTestRunDir) 90 | verilog should containStr ("assign outA = {in_2,in_1};") 91 | verilog should containStr ("assign checkA = 1'h1;") 92 | verilog should containStr ("assign checkB = 1'h1;") 93 | verilog should containStr ("assign checkC = 1'h1;") 94 | // NB : outB cannot be checked without involving intermediate wires whose names could change 95 | } 96 | 97 | "TestBitPattern" should "properly assign with zeroes and ones" in { 98 | class TestBitPattern() extends RawModule { 99 | val outA = IO(Output(UInt(8.W))) 100 | val outB = IO(Output(UInt(8.W))) 101 | 102 | outA := Zeroes 103 | outB := Ones 104 | } 105 | val verilog = (new ChiselStage()).emitVerilog(new TestBitPattern(), setTestRunDir) 106 | verilog should containStr ("assign outA = 8'h0;") 107 | verilog should containStr ("assign outB = 8'hff;") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /mrproper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Clean ALL compiled files in the project 3 | 4 | # Little trick to preserve target/.history 5 | # s* => scala-/ ; streams/ 6 | rm -rf target/s* 7 | rm -rf project/target 8 | rm -rf project/project/target -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.5.6 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") 2 | // addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1") 3 | // addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") 4 | 5 | // maven central publication 6 | addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") 7 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.10") 8 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") 9 | 10 | // self-contained jar executable publication 11 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.1.0") 12 | addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.3.1") 13 | -------------------------------------------------------------------------------- /project/sbt-antlr4.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.8.1") 2 | -------------------------------------------------------------------------------- /src/main/resources/project/config.yml: -------------------------------------------------------------------------------- 1 | # Use of this source code is governed by a BSD-style 2 | # license that can be found in the LICENSE file. 3 | # Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | # Config file format: list of projects 6 | - name: MyProject #project name, optional 7 | emissionPath: "chisel_gen/my_project" #base path of translated files, optional 8 | basePath: "src/main/resources/project/hdl" #base path of files to be translated, optional 9 | files: #list of files to be translated into chisel, at least one file is required 10 | - my_package.sv 11 | - my_module.sv 12 | 13 | translationOptions: 14 | LegalizeParamDefaults: 15 | legalizeMethod: moveOrOverride # comment|overrideOption|moveOrComment|moveOrOverride 16 | RemoveConcats: 17 | useChiselCat: true 18 | Chiselizer: 19 | toCamelCase: false 20 | ignoreEnumFieldScalastyle: false 21 | unpackedEmissionStyle: Reg # Reg|Mem 22 | topLevelChiselGenerators: # list of top in the project 23 | - name: my_module # name of top 24 | withWrapper: true # provide a wrapper for params and with structural ports 25 | 26 | # baseBlackboxRessourcePath must contains /resources/ to be valid 27 | # - sv files are copied there 28 | # - blackboxes are mixed with HasBlackBoxResource trait and features a addRessource("path") 29 | baseBlackboxRessourcePath: "" 30 | 31 | # - name: MyProjectB 32 | # base_in_path: "" 33 | # base_out_path: "" 34 | # files: 35 | # - my_package.sv 36 | # - my_module.sv 37 | -------------------------------------------------------------------------------- /src/main/resources/project/hdl/my_module.sv: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | import my_package::*; 6 | 7 | module my_module ( 8 | input clock, 9 | input reset, 10 | input [my_width-1:0] a, 11 | input [my_width-1:0] b, 12 | output [my_width:0] c 13 | ); 14 | 15 | logic [my_package::my_width:0] r; 16 | 17 | always_ff @ (posedge clock) begin 18 | if (reset) begin 19 | r <= '0; 20 | end else begin 21 | r <= a + b; 22 | end 23 | end 24 | 25 | assign c = r; 26 | 27 | endmodule // my_module -------------------------------------------------------------------------------- /src/main/resources/project/hdl/my_package.sv: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package my_package; 6 | localparam my_width = 2; 7 | endpackage -------------------------------------------------------------------------------- /src/main/scala/logger/EasyLogger.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package logger 6 | 7 | trait EasyLogging extends LazyLogging with EasyLogger 8 | 9 | trait EasyLogger { 10 | protected def logger : Logger 11 | 12 | // note: octal literals such as \033 are deprecated in scala 13 | private val red = "\u001b[31m" 14 | // private val green = "\u001b[32m" 15 | private val orange = "\u001b[33m" 16 | private val blue = "\u001b[34m" 17 | private val pink = "\u001b[35m" 18 | private val lightblue = "\u001b[36m" 19 | private val white = "\u001b[37m" 20 | private val end = "\u001b[0m" 21 | 22 | /** 23 | * Log msg at Error level 24 | * @param msg msg generator to be invoked if level is right 25 | */ 26 | def fatal(msg: => String): Unit = { 27 | logger.error(s"[${red}fatal${end}] $msg") 28 | } 29 | /** 30 | * Log msg at Error level 31 | * @param msg msg generator to be invoked if level is right 32 | */ 33 | def critical(msg: => String): Unit = { 34 | logger.error(s"[${pink}critical${end}] $msg") 35 | } 36 | /** 37 | * Log msg at Warn level 38 | * @param msg msg generator to be invoked if level is right 39 | */ 40 | def warn(msg: => String): Unit = { 41 | logger.warn(s"[${orange}warn${end}] $msg") 42 | } 43 | /** 44 | * Log msg at Info level 45 | * @param msg msg generator to be invoked if level is right 46 | */ 47 | def struct(msg: => String): Unit = { 48 | logger.struct(s"[log] $msg") 49 | } 50 | /** 51 | * Log msg at Info level 52 | * @param msg msg generator to be invoked if level is right 53 | */ 54 | def info(msg: => String): Unit = { 55 | logger.info(s"[${blue}info${end}] $msg") 56 | } 57 | /** 58 | * Log msg at Debug level 59 | * @param msg msg generator to be invoked if level is right 60 | */ 61 | def debug(msg: => String): Unit = { 62 | logger.debug(s"[${lightblue}debug${end}] $msg") 63 | } 64 | /** 65 | * Log msg at Trace level 66 | * @param msg msg generator to be invoked if level is right 67 | */ 68 | def trace(msg: => String): Unit = { 69 | logger.trace(s"[${white}trace${end}] $msg") 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/main/scala/logger/InfoLogger.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package logger 6 | 7 | import org.antlr.v4.runtime.misc.Interval 8 | import org.antlr.v4.runtime.{CommonTokenStream} 9 | import scala.collection.JavaConverters._ 10 | 11 | trait HasPath { 12 | def path: String 13 | } 14 | 15 | trait InfoLogging extends InfoLogger with LazyLogging 16 | 17 | trait InfoLogger extends EasyLogger { 18 | def currentSourceFile : Option[HasPath] 19 | def currentStream : Option[CommonTokenStream] 20 | 21 | def getAtInfo(i: Interval): String = { 22 | val path = currentSourceFile match { 23 | case Some(src) => s"at ${src.path}" 24 | case None => "" 25 | } 26 | (currentStream, i) match { 27 | case (_, i) if(i.a < 0) => path 28 | case (Some(stream), _) => 29 | val tokens = stream.getTokens(i.a, i.b) 30 | val sl = tokens.asScala.head.getLine() 31 | val stl = tokens.asScala.last.getLine() 32 | val sc = tokens.asScala.head.getCharPositionInLine() 33 | val stc = tokens.asScala.last.getCharPositionInLine() 34 | if (sl == stl) { 35 | if (sc == stc) { 36 | s"${path}:$sl:$sc" 37 | } else { 38 | s"${path}:$sl:$sc->$stc" 39 | } 40 | } else { 41 | s"${path}:$sl:$sc>>$stl:$stc" 42 | } 43 | case (None, _) => path 44 | } 45 | } 46 | 47 | /** 48 | * Log msg at Error level 49 | * @param msg msg generator to be invoked if level is right 50 | */ 51 | def fatal(n: Interval, msg: => String): Unit = { 52 | fatal(s"$msg ${getAtInfo(n)}") 53 | } 54 | /** 55 | * Log msg at Error level 56 | * @param msg msg generator to be invoked if level is right 57 | */ 58 | def critical(n: Interval, msg: => String): Unit = { 59 | critical(s"$msg ${getAtInfo(n)}") 60 | } 61 | /** 62 | * Log msg at Warn level 63 | * @param msg msg generator to be invoked if level is right 64 | */ 65 | def warn(n: Interval, msg: => String): Unit = { 66 | warn(s"$msg ${getAtInfo(n)}") 67 | } 68 | /** 69 | * Log msg at Info level 70 | * @param msg msg generator to be invoked if level is right 71 | */ 72 | def info(n: Interval, msg: => String): Unit = { 73 | info(s"$msg ${getAtInfo(n)}") 74 | } 75 | /** 76 | * Log msg at Debug level 77 | * @param msg msg generator to be invoked if level is right 78 | */ 79 | def debug(n: Interval, msg: => String): Unit = { 80 | debug(s"$msg ${getAtInfo(n)}") 81 | } 82 | /** 83 | * Log msg at Trace level 84 | * @param msg msg generator to be invoked if level is right 85 | */ 86 | def trace(n: Interval, msg: => String): Unit = { 87 | trace(s"$msg ${getAtInfo(n)}") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/scala/logger/LoggerOptions.scala: -------------------------------------------------------------------------------- 1 | // Freely inspired by firrtl LoggerOptions.scala 2 | // Originally retrieved from https://github.com/freechipsproject/firrtl/ March 2020 3 | // See LICENSE.firrtl for license details. 4 | 5 | package logger 6 | 7 | /** Internal options used to control the logging 8 | * 9 | * @param globalLogLevel the verbosity of logging (default: [[logger.LogLevel.None]]) 10 | * @param classLogLevels the individual verbosity of logging for specific classes 11 | * @param logToFile if true, log to a file 12 | * @param logClassNames indicates logging verbosity on a class-by-class basis 13 | */ 14 | class LoggerOptions private [logger] ( 15 | val globalLogLevel: LogLevel.Value = LogLevel.Warn, 16 | val classLogLevels: Map[String, LogLevel.Value] = Map.empty, 17 | val logClassNames: Boolean = false, 18 | val logFileName: Option[String] = None) { 19 | 20 | private [logger] def copy( 21 | globalLogLevel: LogLevel.Value = globalLogLevel, 22 | classLogLevels: Map[String, LogLevel.Value] = classLogLevels, 23 | logClassNames: Boolean = logClassNames, 24 | logFileName: Option[String] = logFileName): LoggerOptions = { 25 | 26 | new LoggerOptions( 27 | globalLogLevel = globalLogLevel, 28 | classLogLevels = classLogLevels, 29 | logClassNames = logClassNames, 30 | logFileName = logFileName) 31 | 32 | } 33 | 34 | /** Return the name of the log file, defaults to `a.log` if unspecified */ 35 | def getLogFileName(): Option[String] = logFileName.orElse(Some("a.log")) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/AppExample.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import logger._ 8 | 9 | // see README for another template example 10 | /** Simple application example using example files provided in resource folder */ 11 | object SV2ChiselAppExample extends App { 12 | Logger.setLevel(LogLevel.Info) 13 | 14 | val basePath = "src/main/resources/project/hdl" 15 | val files = Seq( 16 | "my_package.sv", 17 | "my_module.sv" 18 | ) 19 | val project = Project("project", basePath, files) 20 | 21 | Driver.emitChisel(project, TranslationOptions(), "chisel_gen") 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/Driver.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import sv2chisel.Utils.{time} 8 | import sv2chisel.transforms._ 9 | 10 | import logger._ 11 | 12 | object Driver extends EasyLogging { 13 | 14 | def lintVerilog(project: Project, options: TranslationOptions): Unit = { 15 | val transforms = Seq( 16 | // to do : unused signals, ... ? 17 | new CheckUseBeforeDecl(options), 18 | new CheckScopes(options), 19 | new InferDefLogicClocks(options), 20 | new TypeReferences(options), // warn about undeclared references 21 | new LegalizeParamDefaults(options) // To adapt just to warn about usage ??? "time to switch to chisel " 22 | ) 23 | struct(s"######### Executing ${transforms.size} transforms #########") 24 | val (timeTransforms, _) = time { project.run(transforms) } 25 | struct(s"# Total Elapsed time running transforms : $timeTransforms ms") 26 | } 27 | 28 | def emitChisel( 29 | project: Project, 30 | options: TranslationOptions, 31 | emissionBasePath: String = "chisel_gen", 32 | noFileIO: Boolean = false 33 | ): String = { 34 | val transforms = Seq( 35 | // Initial checks & set-up 36 | new CheckUseBeforeDecl(options), // set a first version of remoteRefs 37 | new CheckScopes(options), 38 | new CheckBlockingAssignments(options), 39 | 40 | // Core transforms 41 | new InferDefLogicClocks(options), 42 | new PropagateClocks(options), 43 | new FlowReferences(options), 44 | new InferUInts(options), // requires flows 45 | new InferParamTypes(options), 46 | new TypeReferences(options), // should run after InferUInts & InferParamTypes for proper type propagation to refs 47 | new LegalizeExpressions(options), // Requires TypedReferences 48 | 49 | // Emission Oriented Transforms 50 | new FixFunctionImplicitReturns(options), 51 | new NameInstancePorts(options), 52 | new RemovePatterns(options), 53 | new RemoveConcats(options), 54 | new AddDontCare(options), 55 | new LegalizeParamDefaults(options), // needs typed parameters 56 | 57 | // Styling transforms 58 | new FixReservedNames(options), 59 | new ToCamelCase(options) 60 | ) 61 | struct(s"######### Executing ${transforms.size} transforms #########") 62 | val (timeTransforms, _) = time { project.run(transforms) } 63 | struct(s"# Total Elapsed time running transforms : $timeTransforms ms") 64 | 65 | struct(s"######### EMISSION #########") 66 | Emitter.emitChisel(ScalaStyleEmission(project, emissionBasePath, options), noFileIO) 67 | } 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/Implicits.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import sv2chisel.ir._ 8 | 9 | object Implicits { 10 | implicit def int2WInt(i: Int): WrappedInt = WrappedInt(UndefinedInterval,BigInt(i)) 11 | implicit def bigint2WInt(i: BigInt): WrappedInt = WrappedInt(UndefinedInterval,i) 12 | } 13 | 14 | case class WrappedInt(tokens: Interval, value: BigInt) { 15 | def U: Expression = UIntLiteral(tokens, value, Width(Utils.getUIntWidth(value)), NumberDecimal) 16 | def S: Expression = SIntLiteral(tokens, value, Width(Utils.getSIntWidth(value))) 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/Parser.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import org.antlr.v4.runtime._ 8 | import org.antlr.v4.runtime.atn._ 9 | import logger.EasyLogging 10 | import sv2chisel.ir._ 11 | import sv2chisel.Utils.time 12 | import sv2chisel.antlr._ 13 | 14 | class ParserException(message: String) extends SV2ChiselUserException(message) 15 | 16 | case class ParameterNotSpecifiedException(message: String) extends ParserException(message) 17 | case class ParameterRedefinedException(message: String) extends ParserException(message) 18 | case class InvalidStringLitException(message: String) extends ParserException(message) 19 | case class InvalidEscapeCharException(message: String) extends ParserException(message) 20 | case class SyntaxErrorsException(message: String) extends ParserException(message) 21 | 22 | 23 | object Parser extends EasyLogging { 24 | 25 | /** Parses a file in a given filename and returns a parsed [[sv2chisel.ir.SourceFile SourceFile]] */ 26 | def parseFile(filename: String, basePath: String="", blackboxes: Boolean = false): (SourceFile, CommonTokenStream) = { 27 | val path = if (basePath != "") basePath + "/" + filename else filename 28 | parseCharStream(CharStreams.fromFileName(path), Some(filename), blackboxes) 29 | } 30 | 31 | /** Parses a String and returns a parsed [[sv2chisel.ir.SourceFile SourceFile]] */ 32 | def parseString(text: String, path: Option[String] = None, blackboxes: Boolean = false): 33 | (SourceFile, CommonTokenStream) = 34 | parseCharStream(CharStreams.fromString(text), path, blackboxes) 35 | 36 | /** Parses a org.antlr.v4.runtime.CharStream and returns a parsed [[sv2chisel.ir.SourceFile SourceFile]] */ 37 | def parseCharStream(charStream: CharStream, path: Option[String] = None, blackboxes: Boolean = false): (SourceFile, CommonTokenStream) = { 38 | struct(s"############# Parsing ${path.getOrElse("")} #############") 39 | val (parseTimeMillis, (cst, tokens)) = time { 40 | val lexer = new sv2017Lexer(charStream) 41 | val tokens = new CommonTokenStream(lexer) 42 | val parser = new sv2017Parser(tokens) 43 | 44 | parser.getInterpreter.setPredictionMode(PredictionMode.SLL) // seems a bit faster ?????? 45 | 46 | // Concrete Syntax Tree 47 | val cst = parser.source_text 48 | 49 | val numSyntaxErrors = parser.getNumberOfSyntaxErrors 50 | if (numSyntaxErrors > 0) throw new SyntaxErrorsException(s"$numSyntaxErrors syntax error(s) detected") 51 | (cst, tokens) 52 | } 53 | 54 | val visitor = new Visitor(DelayedWarning, tokens, path, blackboxes) 55 | val (visitTimeMillis, visit) = time { 56 | visitor.visit(cst) 57 | } 58 | val ast = visit match { 59 | case c: SourceFile => c 60 | case _ => throw new ClassCastException("Error! AST not rooted with SourceFile node!") 61 | } 62 | struct(s"######### Elapsed time for ${path.getOrElse("")} #########") 63 | struct(s"# Lexing+Parsing Time : $parseTimeMillis ms") 64 | struct(s"# Mapping to IR Time : $visitTimeMillis ms") 65 | (ast, tokens) 66 | } 67 | /** Takes Iterator over lines of SV2Chisel, returns SVNode (root node is SourceFile) */ 68 | def parse(lines: Iterator[String]): (SourceFile, CommonTokenStream) = 69 | parseString(lines.mkString("\n")) 70 | 71 | def parse(lines: Seq[String]): (SourceFile, CommonTokenStream) = parseString(lines.mkString("\n")) 72 | 73 | def parse(text: String): (SourceFile, CommonTokenStream) = parseString(text) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ParserLogging.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import org.antlr.v4.runtime.{ParserRuleContext,CommonTokenStream} 8 | import org.antlr.v4.runtime.tree.{TerminalNode} 9 | import scala.collection.JavaConverters._ 10 | 11 | import logger.EasyLogging 12 | 13 | import sv2chisel.ir.{UndefinedInterval, Interval} 14 | 15 | trait ParserLogging extends EasyLogging { 16 | def tokenStream: CommonTokenStream 17 | def pathInfo: String 18 | 19 | def getInfo(ctx: ParserRuleContext): String = { 20 | getInfo(ctx.getSourceInterval()) 21 | } 22 | 23 | def getInfo(n: TerminalNode): String = { 24 | getInfo(n.getSourceInterval()) 25 | } 26 | 27 | def getInfo(i: Interval): String = { 28 | i match { 29 | case UndefinedInterval => s"${pathInfo}:???" 30 | case _ => 31 | val tokens = tokenStream.getTokens(i.a, i.b) 32 | val sl = tokens.asScala.head.getLine() 33 | val stl = tokens.asScala.last.getLine() 34 | // not working as expected : useless absolute char index 35 | // val sc = tokens.asScala.head.getStartIndex() 36 | // val stc = tokens.asScala.last.getStopIndex() 37 | if (sl == stl) { 38 | s"${pathInfo}:$sl" 39 | } else { 40 | s"${pathInfo}:$sl>>$stl" 41 | } 42 | } 43 | } 44 | 45 | def fatal(ctx: ParserRuleContext, msg: => String): Unit = fatal(s"At ${getInfo(ctx)}: $msg") 46 | def fatal(n: TerminalNode, msg: => String): Unit = fatal(s"At ${getInfo(n)}: $msg") 47 | def fatal(i: Interval, msg: => String): Unit = fatal(s"At ${getInfo(i)}: $msg") 48 | 49 | def critical(ctx: ParserRuleContext, msg: => String): Unit = critical(s"At ${getInfo(ctx)}: $msg") 50 | def critical(n: TerminalNode, msg: => String): Unit = critical(s"At ${getInfo(n)}: $msg") 51 | def critical(i: Interval, msg: => String): Unit = critical(s"At ${getInfo(i)}: $msg") 52 | 53 | def warn(ctx: ParserRuleContext, msg: => String): Unit = warn(s"At ${getInfo(ctx)}: $msg") 54 | def warn(n: TerminalNode, msg: => String): Unit = warn(s"At ${getInfo(n)}: $msg") 55 | def warn(i: Interval, msg: => String): Unit = warn(s"At ${getInfo(i)}: $msg") 56 | 57 | def struct(ctx: ParserRuleContext, msg: => String): Unit = struct(s"At ${getInfo(ctx)}: $msg") 58 | def struct(n: TerminalNode, msg: => String): Unit = struct(s"At ${getInfo(n)}: $msg") 59 | def struct(i: Interval, msg: => String): Unit = struct(s"At ${getInfo(i)}: $msg") 60 | 61 | def info(ctx: ParserRuleContext, msg: => String): Unit = info(s"At ${getInfo(ctx)}: $msg") 62 | def info(n: TerminalNode, msg: => String): Unit = info(s"At ${getInfo(n)}: $msg") 63 | def info(i: Interval, msg: => String): Unit = info(s"At ${getInfo(i)}: $msg") 64 | 65 | def debug(ctx: ParserRuleContext, msg: => String): Unit = debug(s"At ${getInfo(ctx)}: $msg") 66 | def debug(n: TerminalNode, msg: => String): Unit = debug(s"At ${getInfo(n)}: $msg") 67 | def debug(i: Interval, msg: => String): Unit = debug(s"At ${getInfo(i)}: $msg") 68 | 69 | def trace(ctx: ParserRuleContext, msg: => String): Unit = trace(s"At ${getInfo(ctx)}: $msg") 70 | def trace(n: TerminalNode, msg: => String): Unit = trace(s"At ${getInfo(n)}: $msg") 71 | def trace(i: Interval, msg: => String): Unit = trace(s"At ${getInfo(i)}: $msg") 72 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/Project.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import sv2chisel.ir._ 8 | import sv2chisel.transforms.{Transform} 9 | import sv2chisel.Utils.{time} 10 | 11 | import logger.{EasyLogging, Logger} 12 | 13 | import org.antlr.v4.runtime.{CommonTokenStream} 14 | import collection.mutable.{HashMap, ArrayBuffer} 15 | import scala.annotation.tailrec 16 | 17 | case class ProjectEntry( 18 | basePath: String, 19 | src: SourceFile, 20 | stream: CommonTokenStream, 21 | blackboxes: Boolean 22 | ) 23 | 24 | class Project(name: String) extends EasyLogging { 25 | 26 | /** 27 | * SOURCES MANAGEMENT PART 28 | */ 29 | private val sources = ArrayBuffer[ProjectEntry]() 30 | 31 | def getEntries: Seq[ProjectEntry] = sources.toSeq 32 | 33 | def addEntry(e: ProjectEntry): Unit = sources += e 34 | 35 | def addFiles(files: Seq[String], basePath: String = "", blackboxes: Boolean = false): Unit = { 36 | files.foreach(f => { 37 | val (src, tokens) = Parser.parseFile(f, basePath, blackboxes) 38 | src.foreachDescription(d => { 39 | d match { 40 | case IsolatedStatement(_, IncludeHeader(_, s)) => addFiles(Seq(s)) 41 | // to do : hierarchy mode 42 | // add submodules files found in path & not already in project 43 | // same for packages 44 | // .... 45 | case _ => 46 | } 47 | }) 48 | sources += ProjectEntry(basePath, src, tokens, blackboxes = blackboxes) 49 | }) 50 | } 51 | 52 | /** 53 | * Processing part 54 | */ 55 | 56 | def foreachDescription(f: Description => Unit): Unit = sources.foreach(_.src.foreachDescription(f)) 57 | def foreachEntry(f: ProjectEntry => Unit): Unit = sources.foreach(f) 58 | 59 | // NB: always refer to latest sources values (do not use zipWithIndex for example) 60 | def mapDescription(f: Description => Description): Unit = 61 | (0 until sources.length).foreach(i => sources(i) = sources(i).copy(src = sources(i).src.mapDescription(f))) 62 | 63 | def mapEntry(f: ProjectEntry => ProjectEntry): Unit = 64 | (0 until sources.length).foreach(i => sources(i) = f(sources(i))) 65 | 66 | /** 67 | * RUN TRANSFORMS 68 | */ 69 | 70 | def runTimed(t: Transform): Unit = { 71 | val log = new Logger(t.getClass.getName) 72 | log.trace(this.serialize) 73 | struct(s" ####### ${t.getClass.getName} #######") 74 | val (timeT, _) = time { 75 | t.execute(this) 76 | } 77 | log.debug(" ### Transform result:\n" + this.serialize) 78 | struct(s" # Elapsed time : $timeT ms") 79 | } 80 | 81 | @tailrec 82 | final def run(t: Seq[Transform]): Unit = { 83 | t match { 84 | case Seq() => 85 | case s => runTimed(s.head); run(s.tail) 86 | } 87 | } 88 | 89 | 90 | /** 91 | * CACHE PART 92 | */ 93 | private val descriptionCache = HashMap[String, Description]() 94 | private var cached = false 95 | 96 | def findDescription(des: String): Option[Description] = { 97 | if(cached){ 98 | debug(s"Keys in description cache: ${descriptionCache.keys}") 99 | if(descriptionCache.contains(des)){ 100 | Some(descriptionCache(des)) 101 | } else { 102 | None 103 | } 104 | } else { 105 | cached = true 106 | sources.map(_.src.descriptions.collect { 107 | case n: HasName => n 108 | }).flatten.foreach {( d => { 109 | if (descriptionCache.contains(d.name)) { 110 | Utils.throwInternalError(s"Multiple descriptions with Name ${d.name} within project $name") 111 | } 112 | descriptionCache += ((d.name, d)) 113 | } 114 | )} 115 | findDescription(des) 116 | } 117 | } 118 | 119 | def findModule(des: String): Option[DefModule] = { 120 | findDescription(des) match { 121 | case None => None 122 | case Some(d: DefModule) => Some(d) 123 | case _ => None 124 | } 125 | } 126 | 127 | /** NB: might conflict with current transform if affecting the same ProjectEntry */ 128 | def updateDescription(name: String, f: Description => Description): Unit = { 129 | var count = 0 130 | sources.zipWithIndex.map(t => { 131 | val e = t._1.copy(src = t._1.src.mapDescription(d => { 132 | d match { 133 | case des: HasName if (des.name == name) => 134 | val updated = f(des) 135 | count += 1 136 | if(cached) 137 | descriptionCache(name) = updated 138 | updated 139 | case _ => d 140 | } 141 | })) 142 | sources(t._2) = e 143 | }) 144 | count match { 145 | case 0 => critical(s"Nothing updated as description $name was not found.") 146 | case 1 => // OK 147 | case _ => critical(s"More than one description ($count) was affected by the update of description $name.") 148 | } 149 | } 150 | 151 | def isProjectDescription(des: String): Boolean = { 152 | findDescription(des) match { 153 | case None => false 154 | case _ => true 155 | } 156 | } 157 | 158 | def isProjectModule(module: String): Boolean = { 159 | findDescription(module) match { 160 | case None => false 161 | case Some(_: DefModule) => true 162 | case _ => false 163 | } 164 | } 165 | 166 | def clearDescriptionCache(): Unit = { 167 | cached = false 168 | descriptionCache.clear() 169 | } 170 | 171 | def serialize : String = sources.map(_.src.serialize).mkString("\n") 172 | 173 | } 174 | 175 | object Project { 176 | def apply(name: String, basePath: String, files: Seq[String], blackboxes: Seq[String] = Seq()): Project = { 177 | val p = new Project(name) 178 | p.addFiles(files, basePath, blackboxes = false) 179 | p.addFiles(blackboxes, basePath, blackboxes = true) 180 | p 181 | } 182 | // Minimal version for single source tests 183 | def apply(name: String, rawVerilog: String, path: Option[String]) = { 184 | val p = new Project(name) 185 | val (src, stream) = Parser.parseString(rawVerilog, path) 186 | p.addEntry(ProjectEntry("raw", src, stream, blackboxes = false)) 187 | p 188 | } 189 | def apply(name: String, rawBlackboxes: String, rawVerilog: String, path: Option[String]) = { 190 | val p = new Project(name) 191 | val (srcB, streamB) = Parser.parseString(rawBlackboxes, path, blackboxes = true) 192 | p.addEntry(ProjectEntry(".", srcB, streamB, blackboxes = true)) 193 | val (src, stream) = Parser.parseString(rawVerilog, path) 194 | p.addEntry(ProjectEntry(".", src, stream, blackboxes = false)) 195 | p 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/SV2ChiselException.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import scala.util.control.NoStackTrace 8 | 9 | /** Exception indicating user error 10 | * 11 | * These exceptions indicate a problem due to bad input and thus do not include a stack trace. 12 | * This can be extended by custom transform writers. 13 | */ 14 | class SV2ChiselUserException(message: String, cause: Throwable = null) 15 | extends RuntimeException(message, cause) with NoStackTrace 16 | 17 | /** Wraps exceptions from CustomTransforms so they can be reported appropriately */ 18 | case class CustomTransformException(cause: Throwable) extends Exception("", cause) 19 | 20 | /** Exception indicating something went wrong *within* Firrtl itself 21 | * 22 | * These exceptions indicate a problem inside the compiler and include a stack trace to help 23 | * developers debug the issue. 24 | * 25 | * This class is private because these are issues within Firrtl itself. Exceptions thrown in custom 26 | * transforms are treated differently and should thus have their own structure 27 | */ 28 | private[sv2chisel] class SV2ChiselInternalException(message: String, cause: Throwable = null) 29 | extends Exception(message, cause) 30 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/TranslationOptions.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | import io.circe._ 8 | import logger.EasyLogging 9 | 10 | // helpers for enum 11 | trait EnumDecoder { 12 | this : Enumeration => 13 | def ctx: String 14 | def decode: Decoder[this.Value] = Decoder.instance(c => { 15 | c.as[String] match { 16 | case Right(s) => 17 | try { 18 | Right(this.withName(s)) 19 | } catch { 20 | case _: java.util.NoSuchElementException => 21 | Left(DecodingFailure(s"Unknown value $s in $ctx ", c.history)) 22 | } 23 | case Left(DecodingFailure(s, l)) => Left(DecodingFailure(s"Cannot parse properly $ctx (not a string). $s", l)) 24 | } 25 | }) 26 | } 27 | 28 | 29 | 30 | case class RemoveConcatOptions( 31 | useChiselCat: Boolean = true 32 | ){ 33 | /** Decoder for circe parsing from yaml */ 34 | def decode: Decoder[RemoveConcatOptions] = Decoder.instance(c => { 35 | val default = RemoveConcatOptions() 36 | for { 37 | useChiselCat <- c.getOrElse[Boolean]("useChiselCat")(default.useChiselCat) 38 | } yield { 39 | RemoveConcatOptions(useChiselCat) 40 | } 41 | }) 42 | } 43 | 44 | case class TopLevelChiselGenerator( 45 | name: String, 46 | withWrapper: Boolean = true 47 | ){ 48 | /** Decoder for circe parsing from yaml */ 49 | def decode: Decoder[TopLevelChiselGenerator] = Decoder.instance(c => { 50 | for { 51 | name <- c.get[String]("name") 52 | withWrapper <- c.getOrElse[Boolean]("withWrapper")(true) 53 | } yield { 54 | TopLevelChiselGenerator(name, withWrapper) 55 | } 56 | }) 57 | } 58 | 59 | case class ChiselizerOptions( 60 | toCamelCase: Boolean = false, 61 | ignoreEnumFieldScalastyle: Boolean = false, 62 | unpackedEmissionStyle:ChiselizerOptions.UnpackedEmissionStyle.Value = ChiselizerOptions.UnpackedEmissionStyle.default, 63 | topLevelChiselGenerators: Seq[TopLevelChiselGenerator] = Seq(), 64 | baseBlackboxRessourcePath: Option[String] = None 65 | ) extends EasyLogging { 66 | /** Decoder for circe parsing from yaml */ 67 | def decode: Decoder[ChiselizerOptions] = Decoder.instance(c => { 68 | val default = ChiselizerOptions() 69 | implicit val d1 = ChiselizerOptions.UnpackedEmissionStyle.decode 70 | implicit val d2 = TopLevelChiselGenerator("").decode 71 | for { 72 | unpackedEmissionStyle <- c.getOrElse[ChiselizerOptions.UnpackedEmissionStyle.Value]("unpackedEmissionStyle")(default.unpackedEmissionStyle) 73 | topLevelChiselGenerators <- c.getOrElse[Seq[TopLevelChiselGenerator]]("topLevelChiselGenerators")(Seq()) 74 | baseBlackboxRessourcePath <- c.getOrElse[String]("baseBlackboxRessourcePath")("") 75 | toCamelCase <- c.getOrElse[Boolean]("toCamelCase")(default.toCamelCase) 76 | ignoreEnumFieldScalastyle <- c.getOrElse[Boolean]("ignoreEnumFieldScalastyle")(default.ignoreEnumFieldScalastyle) 77 | } yield { 78 | val bbPath = baseBlackboxRessourcePath match { 79 | case "" => None 80 | case s if(s.contains("/resources/")) => Some(s) 81 | case _ => 82 | critical(s"Ignoring non-compliant baseBlackboxRessourcePath $baseBlackboxRessourcePath (must contain /resources/)") 83 | None 84 | 85 | } 86 | ChiselizerOptions(toCamelCase, ignoreEnumFieldScalastyle, unpackedEmissionStyle, topLevelChiselGenerators, bbPath) 87 | } 88 | }) 89 | } 90 | object ChiselizerOptions { 91 | object UnpackedEmissionStyle extends Enumeration with EnumDecoder { 92 | val Reg, Mem = Value 93 | def default = Mem 94 | def ctx = "unpackedEmissionStyle for Chiselizer" 95 | } 96 | } 97 | 98 | 99 | case class LegalizeParamDefaultOptions( 100 | legalizeMethod: LegalizeParamDefaultOptions.LegalizeMethod.Value = LegalizeParamDefaultOptions.LegalizeMethod.default 101 | ){ 102 | /** Decoder for circe parsing from yaml */ 103 | def decode: Decoder[LegalizeParamDefaultOptions] = Decoder.instance(c => { 104 | val default = LegalizeParamDefaultOptions() 105 | implicit val decoder = LegalizeParamDefaultOptions.LegalizeMethod.decode 106 | for { 107 | legalizeMethod <- c.getOrElse[LegalizeParamDefaultOptions.LegalizeMethod.Value]("legalizeMethod")(default.legalizeMethod) 108 | } yield { 109 | LegalizeParamDefaultOptions(legalizeMethod) 110 | } 111 | }) 112 | } 113 | 114 | 115 | object LegalizeParamDefaultOptions { 116 | object LegalizeMethod extends Enumeration with EnumDecoder { 117 | val comment, overrideOption, moveOrComment, moveOrOverride = Value 118 | def default = moveOrOverride 119 | def ctx = "legalizeMethod for LegalizeParamDefaults" 120 | } 121 | } 122 | 123 | case class TranslationOptions( 124 | legalizeParamDefault: LegalizeParamDefaultOptions = LegalizeParamDefaultOptions(), 125 | removeConcat: RemoveConcatOptions = RemoveConcatOptions(), 126 | chiselizer: ChiselizerOptions = ChiselizerOptions() 127 | ) { 128 | /** Decoder for circe parsing from yaml */ 129 | def decode: Decoder[TranslationOptions] = Decoder.instance(c => { 130 | val default = TranslationOptions() 131 | implicit val d1 = LegalizeParamDefaultOptions().decode 132 | implicit val d2 = RemoveConcatOptions().decode 133 | implicit val d3 = ChiselizerOptions().decode 134 | for { 135 | legalizeParamDefault <- c.getOrElse[LegalizeParamDefaultOptions]("LegalizeParamDefaults")(default.legalizeParamDefault) 136 | removeConcat <- c.getOrElse[RemoveConcatOptions]("RemoveConcats")(default.removeConcat) 137 | chiselizer <- c.getOrElse[ChiselizerOptions]("Chiselizer")(default.chiselizer) 138 | } yield { 139 | TranslationOptions(legalizeParamDefault, removeConcat, chiselizer) 140 | } 141 | }) 142 | } 143 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ir/EvalExpression.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package ir 7 | 8 | import ir.PrimOps._ 9 | 10 | package object evalExpression { 11 | implicit def expressionToEvalExpression(e: Expression) = new EvalExpression(e) 12 | } 13 | 14 | class EvalExpression(e: Expression) { 15 | val ui = UndefinedInterval 16 | 17 | private def evalBinOp(s: Seq[Expression], f:(BigInt, BigInt)=> BigInt): Option[BigInt] = { 18 | s match { 19 | case Seq(a, b) => 20 | (recEvalBigInt(a), recEvalBigInt(b)) match { 21 | case (Some(e1), Some(e2)) => Some(f(e1, e2)) 22 | case _ => None 23 | } 24 | case _ => None 25 | } 26 | } 27 | 28 | private def evalBoolOp(s: Seq[Expression], f:(BigInt, BigInt)=> Boolean): Option[BigInt] = { 29 | def tmp(a: BigInt, b: BigInt): BigInt = if(f(a, b)) BigInt(1) else BigInt(0) 30 | evalBinOp(s, tmp) 31 | } 32 | 33 | private def evalUnaryOp(s: Seq[Expression], f:(BigInt) => BigInt): Option[BigInt] = { 34 | s match { 35 | case Seq(e) => 36 | (recEvalBigInt(e)) match { 37 | case (Some(e1)) => Some(f(e1)) 38 | case _ => None 39 | } 40 | case _ => None 41 | } 42 | } 43 | 44 | private def recEvalBigInt(e: Expression): Option[BigInt] = { 45 | e match { 46 | // supported primitives 47 | case n: Number => n.getBigInt 48 | case l :Literal => Some(l.value) 49 | 50 | // DoPrim 51 | case DoPrim(_, Par(_), s, _, _) => evalUnaryOp(s, bi => bi) 52 | 53 | case DoPrim(_, Mul(_), s, _, _) => evalBinOp(s, _*_) 54 | case DoPrim(_, Pow(_), s, _, _) => 55 | evalBinOp(s, (a, b) => BigInt(math.pow(a.toDouble,b.toDouble).toLong)) 56 | case DoPrim(_, Div(_), s, _, _) => evalBinOp(s, _/_) 57 | case DoPrim(_, Rem(_), s, _, _) => evalBinOp(s, _%_) 58 | case DoPrim(_, Add(_), s, _, _) => evalBinOp(s, _+_) 59 | case DoPrim(_, Sub(_), s, _, _) => evalBinOp(s, _-_) 60 | 61 | case DoPrim(_, Lt(_), s, _, _) => evalBoolOp(s, _<_) 62 | case DoPrim(_, Leq(_), s, _, _) => evalBoolOp(s, _<=_) 63 | case DoPrim(_, Gt(_), s, _, _) => evalBoolOp(s, _>_) 64 | case DoPrim(_, Geq(_), s, _, _) => evalBoolOp(s, _>=_) 65 | case DoPrim(_, Eq(_), s, _, _) => evalBoolOp(s, _==_) 66 | case DoPrim(_, Neq(_), s, _, _) => evalBoolOp(s, _!=_) 67 | 68 | case DoPrim(_, Not(_), Seq(expr), _, _) => 69 | recEvalBigInt(expr) match { 70 | case None => None 71 | case Some(v) if (v == 0) => Some(BigInt(1)) 72 | case _ => Some(BigInt(0)) 73 | } 74 | 75 | case DoPrim(_, And(_), Seq(e1, e2), _, _) => 76 | (recEvalBigInt(e1), recEvalBigInt(e2)) match { 77 | case (None, _) => None 78 | case (_, None) => None 79 | case (Some(a), Some(b)) if(a == 1 && b == 1) => Some(BigInt(1)) 80 | case _ => Some(BigInt(0)) 81 | } 82 | case DoPrim(_, Or(_), Seq(e1, e2), _, _) => 83 | (recEvalBigInt(e1), recEvalBigInt(e2)) match { 84 | case (None, _) => None 85 | case (_, None) => None 86 | case (Some(a), Some(b)) if(a == 0 && b == 0) => Some(BigInt(0)) 87 | case _ => Some(BigInt(1)) 88 | } 89 | 90 | case DoPrim(_, BitNeg(_), s, _, _) => evalUnaryOp(s, ~_) 91 | case DoPrim(_, BitAnd(_), s, _, _) => evalBinOp(s, _&_) 92 | case DoPrim(_, BitOr(_), s, _, _) => evalBinOp(s, _|_) 93 | case DoPrim(_, BitXor(_), s, _, _) => evalBinOp(s, _^_) 94 | 95 | case DoPrim(_, Shl(_), s, _, _) => evalBinOp(s, (a, b) => a << b.toInt) 96 | case DoPrim(_, Shr(_), s, _, _) => evalBinOp(s, (a, b) => a >> b.toInt) 97 | case DoPrim(_, LogShr(_), s, _, _) => evalBinOp(s, (a, b) => a.toInt >>> b.toInt) 98 | 99 | case DoPrim(_, Plus(_), s, _, _) => evalUnaryOp(s, bi => bi) 100 | case DoPrim(_, Minus(_), s, _, _) => evalUnaryOp(s, -_) 101 | 102 | case DoPrim(_, CeilLog2(_), s, _, _) => evalUnaryOp(s, in => (in-1).bitLength) 103 | case DoPrim(_, InlineIf(_), s, _, _) => 104 | s match { 105 | case Seq(a, b, c) => 106 | recEvalBigInt(a) match { 107 | case None => None 108 | case Some(v) if (v == 0) => recEvalBigInt(c) 109 | case _ => recEvalBigInt(b) 110 | } 111 | case _ => None 112 | } 113 | // Note : concat is not supported 114 | case _ => None 115 | } 116 | } 117 | 118 | def evalBigIntOption: Option[BigInt] = recEvalBigInt(e) 119 | def evalBigInt: BigInt = this.evalBigIntOption.get 120 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ir/ExpressionToLiteral.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package ir 7 | 8 | import logger.{InfoLogging} 9 | import org.antlr.v4.runtime.{CommonTokenStream} 10 | 11 | 12 | package object expressionToLiteral { 13 | implicit def expToLiteral(e: Expression) = new ExpressionToLiteral(e) 14 | } 15 | 16 | class ExpressionToLiteral(e: Expression) extends InfoLogging { 17 | var currentSourceFile : Option[SourceFile] = None 18 | var currentStream : Option[CommonTokenStream] = None 19 | implicit def svnode2Interval(n:SVNode): Interval = n.tokens 20 | 21 | val ui = UndefinedInterval 22 | 23 | def toLiteralOption(implicit currentSourceFile : Option[SourceFile], currentStream : Option[CommonTokenStream]): Option[Expression] = { 24 | this.currentSourceFile = currentSourceFile 25 | this.currentStream = currentStream 26 | e match { 27 | case n: Number => 28 | n.getBigInt match { 29 | case Some(bg) if (bg < 0) => 30 | n.base match { 31 | case NumberDecimal => Some(SIntLiteral(n.tokens, bg, n.width)) 32 | case b => 33 | warn(n, s"[fixable] Unable to convert negative numbers with base $b to Literal") 34 | None 35 | } 36 | 37 | case Some(bg) => Some(UIntLiteral(n.tokens, bg, n.width, n.base)) 38 | 39 | case _ => 40 | warn(n, s"Unable to convert ${n.serialize} to BigInt") 41 | None 42 | } 43 | 44 | case r: Reference => 45 | r.tpe match { 46 | case i:IntType => 47 | warn(r, s"Tech Debt: casting IntType to UIntType (TODO: proper signed management)") 48 | Some(DoCast(r.tokens, e, HwExpressionKind, UIntType(UndefinedInterval, UnknownWidth(), i.base))) 49 | case _ => 50 | trace(r, s"Reference with tpe: ${r.tpe.serialize}") 51 | None 52 | } 53 | 54 | case _ => 55 | trace(e, s"Cannot convert expression ${e.serialize} to Literal") 56 | None 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ir/ExpressionWidth.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package ir 7 | 8 | import logger.{InfoLogging} 9 | import org.antlr.v4.runtime.{CommonTokenStream} 10 | 11 | package object expressionWidth { 12 | implicit def expressionToExpressionWidth(e: Expression) = new ExpressionWidth(e) 13 | } 14 | 15 | import expressionWidth._ 16 | 17 | class ExpressionWidth(e: Expression) extends InfoLogging { 18 | var currentSourceFile : Option[SourceFile] = None 19 | var currentStream : Option[CommonTokenStream] = None 20 | implicit def svnode2Interval(n:SVNode): Interval = n.tokens 21 | val ui = UndefinedInterval 22 | 23 | def getWidthOption( 24 | implicit currentSourceFile : Option[SourceFile], 25 | currentStream : Option[CommonTokenStream] 26 | ): Option[Expression] = { 27 | this.currentSourceFile = currentSourceFile 28 | this.currentStream = currentStream 29 | 30 | e.tpe.widthOption match { 31 | case Some(w) => Some(w.expr) 32 | case None => 33 | e match { 34 | case c: Concat => 35 | val args = c.args.map(_.getWidthOption) 36 | args.collect {case a@None => a} match { 37 | case Seq() => 38 | // OK continue 39 | Some(args.map(_.get).reduce((a, b) => { 40 | DoPrim(ui, PrimOps.Add(ui), Seq(a, b)) 41 | })) 42 | case _ => None 43 | } 44 | 45 | 46 | // case x: DoPrim => 47 | // case x: Reference => 48 | // case x: DoCast => 49 | // case x: DoCall => 50 | // case x: SubField => 51 | // case x: SubIndex => 52 | // case x: SubRange => 53 | // case x: Number => 54 | // case x: Assign => 55 | // case x: MappedValues => 56 | // case x: SeqValues => 57 | // case x: ReplicatePattern => 58 | // case x: UIntLiteral => 59 | // case x: SIntLiteral => 60 | // case x: BoolLiteral => 61 | // case x: RawScalaExpression => 62 | // case x: RawScalaExprWrapper => 63 | // case x: TypeInst => 64 | // case x: DontCare => 65 | // case x: StringLit => 66 | // case x: FillingBitPattern => 67 | // case x: AssignPattern => 68 | // case x: MaskedNumber => 69 | // case x: FixedLiteral => 70 | // case x: DefaultAssignPattern => 71 | // case x: UndefinedExpression => 72 | 73 | case _ => 74 | warn(e, s"Unsupported width inference for: ${e.serialize}: <${e.tpe.serialize}>(${e.tpe.getClass.getName})") 75 | None 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ir/WidthExpressionTypes.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package ir 7 | 8 | import logger.{InfoLogging} 9 | import org.antlr.v4.runtime.{CommonTokenStream} 10 | 11 | 12 | package object widthExpressionType { 13 | implicit def typeToWithExpressionType(t: Type) = new WidthExpressionType(t) 14 | } 15 | 16 | import widthExpressionType._ 17 | 18 | class WidthExpressionType(t: Type) extends InfoLogging { 19 | var currentSourceFile : Option[SourceFile] = None 20 | var currentStream : Option[CommonTokenStream] = None 21 | implicit def svnode2Interval(n:SVNode): Interval = n.tokens 22 | 23 | val ui = UndefinedInterval 24 | 25 | def getWidthExpression(implicit currentSourceFile : Option[SourceFile], currentStream : Option[CommonTokenStream]): Expression = { 26 | this.currentSourceFile = currentSourceFile 27 | this.currentStream = currentStream 28 | t match { 29 | case v: VecType => 30 | v.tpe match { 31 | case Seq(_: BoolType) => v.getLen // avoid a useless mul by one 32 | case Seq(t) => DoPrim(ui, PrimOps.Mul(ui), Seq(v.getLen, t.getWidthExpression)) 33 | case _ => 34 | fatal(v, "Unsupported MixedVec Type for width expression calculation") 35 | UndefinedExpression(ui) 36 | } 37 | case b: BundleType => 38 | DoPrim(ui, PrimOps.GetWidth(ui), Seq(TypeInst(ui, b, None, Seq(), HwExpressionKind, UnknownFlow)), SwExpressionKind, IntType()) 39 | 40 | case tpe: GroundType => tpe.width.expr 41 | case u: UserRefType => 42 | DoPrim(ui, PrimOps.GetWidth(ui), Seq(TypeInst(ui, u.tpe, Some(u.name), u.path, HwExpressionKind, SourceFlow)), SwExpressionKind, IntType()) 43 | 44 | case _ => 45 | fatal(t, s"Unsupported Type for width expression calculation: ${t}") 46 | UndefinedExpression(ui) 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/ir/ir_package.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | 7 | package object ir { 8 | type Interval = org.antlr.v4.runtime.misc.Interval // avoid import in all files 9 | implicit def svnode2Interval[T <: SVNode](n:T): Interval = n.tokens 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/AddDontCare.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | class AddDontCare(val options: TranslationOptions) extends DescriptionBasedTransform { 11 | 12 | 13 | def processDescription(d: Description): Description = { 14 | d match { 15 | case m: Module => m.mapStmt(processStatement) 16 | case d => d 17 | } 18 | } 19 | 20 | 21 | def processPortMap(assign: Seq[Assign], ref: Seq[(Port, Boolean)], ctx: String): Seq[Assign] = { 22 | // process ports 23 | val nna = assign.collect({case nna:NoNameAssign => nna}) 24 | val na = assign.collect({case na:NamedAssign => na}) 25 | val aa = assign.collect({case aa:AutoAssign => aa}) 26 | (aa, nna, na) match { 27 | case (Seq(), Seq(), Seq()) => assign 28 | case (aa, Seq(), Seq()) => critical(aa.head, s"Unsupported auto-assign $ctx"); assign 29 | case (Seq(), nna, Seq()) => 30 | if(nna.length < ref.length){ 31 | assign ++ ref.drop(nna.length).flatMap { case (p, isClock) => p.direction match { 32 | case _:Input if(!isClock) => 33 | warn(p, s"Adding DontCare for port ${p.name} $ctx") 34 | Some(NoNameAssign(UndefinedInterval, DontCare(UndefinedInterval), SourceFlow)) 35 | case _ => None 36 | }} 37 | } else { 38 | assign 39 | } 40 | case (Seq(), Seq(), na) => 41 | val mapped = na.groupBy(_.name) 42 | na ++ ref.flatMap { case (p, isClock) => p.direction match { 43 | case _: Input if(!mapped.contains(p.name) && !isClock) => 44 | warn(p, s"Adding DontCare for port ${p.name} $ctx") 45 | Some(NamedAssign(UndefinedInterval, p.name, DontCare(UndefinedInterval), SourceFlow)) 46 | case _ => None 47 | }} 48 | 49 | case _ => critical(assign.head, s"Unsupported mixed assign methods $ctx"); assign 50 | } 51 | } 52 | 53 | def processStatement(s: Statement): Statement = { 54 | trace(s, s"Entering processStatement for ${s.getClass.getName}") 55 | s.mapStmt(processStatement) match { 56 | case i: DefInstance => 57 | // let's fetch remote module if known 58 | currentProject.get.findModule(i.module.serialize) match { 59 | case Some(m) => 60 | val ctx = s"for instance `${i.name}` of module `${i.module.serialize}`" 61 | val portsNoClock = m.ports.map(p => p -> m.clock.map(_ == p.name).getOrElse(false)) 62 | i.copy(portMap = processPortMap(i.portMap, portsNoClock, ctx)) 63 | 64 | case _ => i 65 | } 66 | 67 | case st => st 68 | } 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/CheckBlockingAssignments.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | /** Resolve logic as wire or reg with appropriate clock (TODO: and reset) 11 | * Also legalize port reg 12 | * > input reg raise [fatal] (non-sense) (resolved as input wire port) 13 | * > output reg are resolved with the generation of an intermediate reg and wire 14 | */ 15 | class CheckBlockingAssignments(val options: TranslationOptions) extends DefModuleBasedTransform { 16 | 17 | 18 | def processModule(m: DefModule): DefModule = { 19 | 20 | var inClockedRegion : Boolean = false 21 | 22 | // TODO : retrieve reference target system from firrtl 23 | def getRefs(e: Expression): Seq[Reference] = { 24 | e match { 25 | case r: Reference => Seq(r) 26 | case s: SubField => getRefs(s.expr) 27 | case s: SubIndex => getRefs(s.expr) 28 | case s: SubRange => getRefs(s.expr) 29 | case c: Concat => c.args.flatMap(getRefs) 30 | case _ => 31 | fatal(e, s"Unable to fetch proper reference for ${e.serialize}") 32 | Seq() 33 | } 34 | } 35 | 36 | //Single pass => fill reg2clocks 37 | def visitConnect(c: Connect): Unit = { 38 | (c.blocking, inClockedRegion) match { 39 | case (true, true) => 40 | critical(c, s"Blocking statement in clocked region: ${c.loc.serialize} = ${c.expr.serialize}") 41 | // TO DO => filter a bit more 42 | // getRefs(c.loc).foreach(r => { 43 | // if(reg2clocks.contains(r.name)){ 44 | // val declClock = reg2clocks(r.name) 45 | // if (declClock != clock) { 46 | // Utils.throwInternalError(s"Same declared object ${r.name} driven by at least 2 different clocks: ${declClock} and ${clock}.") 47 | // } 48 | // } else { 49 | // reg2clocks += ((r.name, clock)) 50 | // registerClockUsage(clock, "non blocking assignment") 51 | // } 52 | // }) 53 | case _ => // nothing to do 54 | } 55 | } 56 | 57 | def visitClockRegion(c: ClockRegion): Unit = { 58 | // no checks here: would be duplicated from InferDefLogicClocks 59 | inClockedRegion = true 60 | // Visiting Statements 61 | c.foreachStmt(visitStatement) 62 | inClockedRegion = false 63 | } 64 | 65 | 66 | def visitStatement(s: Statement): Unit = { 67 | s match { 68 | case c: ClockRegion => visitClockRegion(c) 69 | case c: Connect => visitConnect(c) 70 | case _ => s.foreachStmt(visitStatement) 71 | } 72 | } 73 | 74 | // module used for second pass 75 | m.foreachStmt(visitStatement) 76 | m 77 | } 78 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/CheckScopes.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | class CheckScopes(val options: TranslationOptions) extends DescriptionBasedTransform { 11 | implicit var srcFile = currentSourceFile 12 | implicit var stream = currentStream 13 | 14 | def processDescription(d: Description): Description = { 15 | d match { 16 | case m: Module => processModule(m) 17 | case p: DefPackage => processPackage(p) 18 | case d => d 19 | } 20 | } 21 | 22 | // Common functions 23 | 24 | def visitExpression(e: Expression)(implicit refStore: RefStore): Unit = { 25 | e.foreachExpr(visitExpression) 26 | e.foreachType(visitType) 27 | e match { 28 | case r: Reference if(!refStore.contains(r)) => 29 | critical(r, s"Undeclared reference ${r.serialize} at current point, scala will not compile") 30 | case _ => 31 | } 32 | } 33 | 34 | def visitType(t: Type)(implicit refStore: RefStore): Unit = { 35 | t.foreachType(visitType) 36 | t.foreachWidth(_.foreachExpr(visitExpression)) 37 | t match { 38 | case u: UserRefType if(!refStore.contains(u)) => 39 | critical(u, s"Undeclared type ${u.serialize} in current scope, scala will not compile") 40 | case _ => 41 | } 42 | } 43 | 44 | def visitStatement(s: Statement, refStore: RefStore): Unit = { 45 | // record 46 | implicit val current = refStore 47 | processImportStatement(s, refStore) match { 48 | // special case For Gen : first named assign declared the variable to be used later on 49 | case f: ForGen => 50 | f.init match { 51 | case na: NamedAssign => refStore += ((WRef(na.name), FullType(IntType(na.tokens, NumberDecimal), SwExpressionKind))) 52 | case _ => 53 | } 54 | val localStore = new RefStore() 55 | localStore ++= refStore 56 | f.foreachExpr(visitExpression) 57 | f.foreachType(visitType) 58 | f.foreachStmt(visitStatement(_, localStore)) 59 | 60 | case _ => 61 | // nb: no recursivity is allowed in verilog so it makes sense to do the check before 62 | s.foreachExpr(visitExpression) 63 | s.foreachType(visitType) 64 | s match { 65 | case l: DefLogic => refStore += ((WRef(l.name), FullType(l.tpe, HwExpressionKind))) 66 | case p: DefParam => refStore += ((WRef(p.name), FullType(p.tpe, p.kind))) 67 | case t: DefType => 68 | refStore += ((WRef(t.name), FullType(t.tpe, HwExpressionKind))) 69 | t.tpe match { 70 | case e: EnumType => e.fields.foreach(f => { 71 | refStore += ((WRef(f.name), FullType(e.tpe, e.kind))) // weird but seems standard to flatten 72 | refStore += ((WRef(f.name, Seq(t.name)), FullType(e.tpe, e.kind))) // not sure if used in this way ? 73 | }) 74 | case _ => 75 | } 76 | case f: DefFunction => refStore += ((WRef(f.name), FullType(f.tpe, HwExpressionKind))) 77 | case p: Port => refStore += ((WRef(p.name), FullType(p.tpe, HwExpressionKind))) 78 | case i: IfGen => 79 | val localStore = new RefStore() 80 | localStore ++= refStore 81 | i.foreachStmt(visitStatement(_, localStore)) 82 | case _ => s.foreachStmt(visitStatement(_, refStore)) 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Processing Packages References 89 | */ 90 | def processPackage(p: DefPackage): DefPackage = { 91 | val refs = new RefStore() 92 | refs ++= remoteRefs 93 | // propagate to local refs & record them 94 | visitStatement(p.body, refs) 95 | p // update of refs is done by check before use 96 | } 97 | 98 | /** 99 | * Processing Module References 100 | */ 101 | def processModule(m: Module): Module = { 102 | val ref2Type = new RefStore() 103 | ref2Type ++= remoteRefs 104 | 105 | m.foreachParam(p => ref2Type += ((WRef(p.name), FullType(p.tpe, SwExpressionKind)))) 106 | visitStatement(m.body, ref2Type) 107 | m 108 | 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/CheckUseBeforeDecl.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | class CheckUseBeforeDecl(val options: TranslationOptions) extends DescriptionBasedTransform { 11 | implicit var srcFile = currentSourceFile 12 | implicit var stream = currentStream 13 | 14 | def processDescription(d: Description): Description = { 15 | d match { 16 | case m: Module => processModule(m) 17 | case p: DefPackage => processPackage(p) 18 | case d => d 19 | } 20 | } 21 | 22 | // Common functions 23 | 24 | def visitExpression(e: Expression)(implicit refStore: RefStore): Unit = { 25 | e.foreachExpr(visitExpression) 26 | e.foreachType(visitType) 27 | e match { 28 | case r: Reference if(!refStore.contains(r)) => 29 | critical(r, s"Undeclared reference ${r.serialize} at current point, scala will not compile") 30 | case _ => 31 | } 32 | } 33 | 34 | def visitType(t: Type)(implicit refStore: RefStore): Unit = { 35 | t.foreachType(visitType) 36 | t.foreachWidth(_.foreachExpr(visitExpression)) 37 | t match { 38 | case u: UserRefType if(!refStore.contains(u)) => 39 | critical(u, s"Undeclared type ${u.serialize} in current scope, scala will not compile") 40 | case _ => 41 | } 42 | } 43 | 44 | def visitStatement(s: Statement)(implicit refStore: RefStore, description: String): Unit = { 45 | // record 46 | processImportStatement(s, refStore) match { 47 | case l: DefLogic => refStore += ((WRef(l.name), FullType(l.tpe, HwExpressionKind))) 48 | case p: DefParam => refStore += ((WRef(p.name), FullType(p.tpe, p.kind))) 49 | case t: DefType => 50 | refStore += ((WRef(t.name), FullType(t.tpe, HwExpressionKind, defDescription = Some(description)))) 51 | t.tpe match { 52 | case e: EnumType => e.fields.foreach(f => { 53 | refStore += ((WRef(f.name), FullType(e.tpe, e.kind))) // weird but seems standard to flatten 54 | refStore += ((WRef(f.name, Seq(t.name)), FullType(e.tpe, e.kind))) // not sure if used in this way ? 55 | }) 56 | case _ => 57 | } 58 | 59 | case f: DefFunction => refStore += ((WRef(f.name), FullType(f.tpe, HwExpressionKind))) 60 | case p: Port => refStore += ((WRef(p.name), FullType(p.tpe, HwExpressionKind))) 61 | case f: ForGen => 62 | f.init match { 63 | case na: NamedAssign => refStore += ((WRef(na.name), FullType(IntType(na.tokens, NumberDecimal), SwExpressionKind))) 64 | case _ => 65 | } 66 | case _ => 67 | } 68 | // visit 69 | s.foreachStmt(visitStatement) 70 | s.foreachExpr(visitExpression) 71 | s.foreachType(visitType) 72 | } 73 | 74 | /** 75 | * Processing Packages References 76 | */ 77 | def processPackage(p: DefPackage): DefPackage = { 78 | implicit val refs = new RefStore() 79 | refs ++= remoteRefs 80 | // propagate to local refs & record them 81 | implicit val description = p.name 82 | visitStatement(p.body) 83 | forceRefsRefresh() // must be done after visitStatement due to the import statement side effect 84 | debug(p, s"Updating refs for package ${p.name}") 85 | p.copy(refs = Some(refs)) 86 | } 87 | 88 | /** 89 | * Processing Module References 90 | */ 91 | def processModule(m: Module): Module = { 92 | implicit val ref2Type = new RefStore() 93 | ref2Type ++= remoteRefs 94 | 95 | //SINGLE PASS => references shall be registered in ref2Type before use 96 | m.foreachParam(p => ref2Type += ((WRef(p.name), FullType(p.tpe, SwExpressionKind)))) 97 | implicit val description = m.name 98 | visitStatement(m.body) 99 | m 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/FixFunctionImplicitReturns.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | /** 11 | * 12 | */ 13 | class FixFunctionImplicitReturns(val options: TranslationOptions) extends DescriptionBasedTransform { 14 | 15 | private def processFunction(f: DefFunction): DefFunction = { 16 | // first pass on function body: lookup connect with function name 17 | var matchingConnects = 0 18 | var lastIsConnect = false 19 | 20 | def visitStatement(s: Statement): Unit = { 21 | s match { 22 | case Connect(_,_,Reference(_,n,_,_,_,_),_,_,_) if(f.name == n) => 23 | matchingConnects += 1 24 | lastIsConnect = true 25 | 26 | case _ => lastIsConnect = true 27 | } 28 | s.foreachStmt(visitStatement) 29 | } 30 | f.foreachStmt(visitStatement) 31 | 32 | debug(f, s"Function `${f.name}` analysis: matching connects:$matchingConnects; lastIsConnect:$lastIsConnect") 33 | 34 | 35 | def removeLastImplicit(s: Statement): Statement = { 36 | s match { 37 | case Connect(_,_,Reference(_,n,_,_,_,_),e,_,_) if(f.name == n) => ExpressionStatement(e) 38 | case _ => s.mapStmt(removeLastImplicit) 39 | } 40 | } 41 | 42 | // second pass on function body: 43 | // either remove the last implicit statement 44 | // or add proper declaration for explicit intermediate connection 45 | if(matchingConnects == 1 && lastIsConnect){ 46 | f.mapStmt(removeLastImplicit) 47 | } else { 48 | val ue = UndefinedExpression() 49 | val returnValueDef = DefLogic(UndefinedInterval, NoVerilogAttribute, f.name, f.tpe, LogicWire(ue)) 50 | val finalReturn = ExpressionStatement(Reference(UndefinedInterval, f.name, Seq(), f.tpe, f.kind, SourceFlow)) 51 | 52 | val body = f.body match { 53 | case b: SimpleBlock => b.copy(stmts = returnValueDef +: b.stmts :+ finalReturn) 54 | case s => SimpleBlock(UndefinedInterval, Seq(returnValueDef, s, finalReturn)) 55 | } 56 | f.copy(body = body) 57 | } 58 | } 59 | 60 | 61 | def processDescription(d: Description): Description = { 62 | def processStatement(s: Statement): Statement = { 63 | s match { 64 | case f: DefFunction => processFunction(f) 65 | case _ => s.mapStmt(processStatement) 66 | } 67 | 68 | } 69 | 70 | d match { 71 | case m: Module => m.mapStmt(processStatement) 72 | case p: DefPackage => p.mapStmt(processStatement) 73 | case _ => d 74 | } 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/FixReservedNames.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | 11 | /** only fixes reset for now until a proper reset inference */ 12 | class FixReservedNames(val options: TranslationOptions) extends DescriptionBasedTransform { 13 | implicit var srcFile = currentSourceFile 14 | implicit var stream = currentStream 15 | 16 | implicit def stringToSnake(s: String) = new SnakeString(s) 17 | 18 | def processDescription(d: Description): Description = { 19 | d match { 20 | case m: Module => m.copy(body = processStatement(m.body)) 21 | case d => d 22 | } 23 | } 24 | 25 | def processExpression(e: Expression): Expression = { 26 | def processRef(r: Reference): Reference = r.path match { 27 | case Seq() if(r.name == "reset") => r.copy(name = "ureset") 28 | case _ => r 29 | } 30 | e match { 31 | case a: Assign => processAssign(a) 32 | case _ => 33 | e.mapExpr(processExpression).mapType(processType).mapWidth(_.mapExpr(processExpression)) match { 34 | case r: TypeInst if(r.name.map(_ == "reset").getOrElse(false)) => r.copy(name = Some("ureset")) 35 | case r: Reference => processRef(r) 36 | case c: DoCall => c.copy(fun = processRef(c.fun)) 37 | case exp => exp 38 | } 39 | } 40 | } 41 | 42 | def processAssign(a: Assign): Assign = { 43 | a.mapExpr(processExpression) match { 44 | case na: NamedAssign => na.copy(assignExpr = na.assignExpr.map(processExpression)) 45 | case na: NoNameAssign => na.copy(assignExpr = na.assignExpr.map(processExpression)) 46 | case aa => aa 47 | } 48 | } 49 | 50 | def processType(t: Type): Type = { 51 | t.mapType(processType).mapWidth(_.mapExpr(processExpression)) match { 52 | case u: UserRefType if(u.name == "reset") => u.copy(name = "ureset") 53 | case tpe => tpe 54 | } 55 | } 56 | 57 | def processStatement(s: Statement): Statement = { 58 | s match { 59 | case i: DefInstance => 60 | val name = if (i.name == "reset") "ureset" else i.name 61 | i.copy(name = name, portMap = i.portMap.map(processAssign(_))) 62 | 63 | case _ => 64 | s.mapStmt(processStatement).mapExpr(processExpression).mapType(processType) match { 65 | case l: DefLogic if (l.name == "reset") => l.copy(name = "ureset") 66 | case p: DefParam if (p.name == "reset") => p.copy(name = "ureset") 67 | case t: DefType if (t.name == "reset") => t.copy(name = "ureset") 68 | case f: DefFunction if (f.name == "reset") => f.copy(name = "ureset") 69 | case p: Port if (p.name == "reset") => p.copy(name = "ureset") 70 | case st => st 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/NameInstancePorts.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | /** 11 | * Ensure that instances use Only Named assigns 12 | * Named assigns are required in chisel and can be inferred only if the 13 | * declaration of instatiated module is available within the current project 14 | */ 15 | class NameInstancePorts( 16 | val options: TranslationOptions 17 | ) extends DefModuleBasedTransform { 18 | 19 | def processModule(m: DefModule): DefModule = { 20 | 21 | // single pass 22 | 23 | def processInstance(i: DefInstance): DefInstance = { 24 | val mName = i.module.serialize 25 | val noName = i.portMap.collect({case na: NoNameAssign => na}) 26 | (noName.length, i.portMap.length) match { 27 | case (0, _) => i 28 | case (l, ll) if (l != ll) => 29 | critical(i, s"Unsupported portmap for instance ${i.name} of module ${i.module} with both named and unnamed port assignations") 30 | i 31 | case (l, _) => 32 | currentProject.get.findModule(mName) match { 33 | case None => 34 | critical(i, s"Unable to find declaration for module ${mName} in current project. Sequential port-map of instance ${i.name} of this module ${mName} cannot be transformed to named port-map which will prevent successful chisel emission. To fix this issue please add the declaration of the module within project files or fix the port map manually") 35 | i 36 | case Some(d) => 37 | if(d.ports.length != l) { 38 | critical(i, s"Instance ${i.name} of module ${mName} has mismatching number of ports ($l) with declaration (${d.ports.length})") 39 | i 40 | } else { 41 | val portMap = noName.zip(d.ports).map(t => t._1.toNamed(t._2.name)) 42 | i.copy(portMap = portMap) 43 | } 44 | } 45 | } 46 | } 47 | 48 | def processStatement(s: Statement): Statement = { 49 | s match { 50 | case i: DefInstance => processInstance(i) 51 | case _ => s.mapStmt(processStatement) 52 | } 53 | } 54 | m.mapStmt(processStatement) 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/scala/sv2chisel/transforms/PropagateClocks.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chisel 6 | package transforms 7 | 8 | import sv2chisel.ir._ 9 | 10 | /** propagate infered clocks to def instance 11 | * 12 | * 13 | * 14 | */ 15 | class PropagateClocks(val options: TranslationOptions) extends DefModuleBasedTransform { 16 | 17 | def processModule(module: DefModule): DefModule = { 18 | 19 | def processInstance(i: DefInstance) = { 20 | currentProject.get.findModule(i.module.name) match { 21 | case Some(m: DefModule) => 22 | 23 | def updatedPortMap(name: String): Seq[Assign] = { 24 | // remove clock from port map 25 | val renamedClock = m.ports.collectFirst{ 26 | case port:Port if(!port.isDefaultClock.isEmpty) => port.isDefaultClock.get 27 | }.getOrElse("") 28 | i.portMap.flatMap(p => { 29 | p match { 30 | case NamedAssign(_, n, _, _, _, _, _) if(n == name || n == renamedClock) => None 31 | case NamedAssign(_, _, r: Reference, _, _, _, _) if(r.serialize == name) => None 32 | case NoNameAssign(_, r: Reference, _, _, _, _, _) if(r.serialize == name) => None 33 | case _ => Some(p) 34 | } 35 | }) 36 | } 37 | 38 | (m.clock, module.clock) match { 39 | case (Some(ci), Some(cm)) if (ci == cm) => i.copy(portMap = updatedPortMap(ci), clock = Some(ci)) 40 | 41 | case (None, Some(cm)) => 42 | // update port map for instance 43 | // NB: proper Clock() & Reset() types are required for instantiation of ExtModules 44 | safeUpdateDescription(m.name, _ => { 45 | i.portMap.flatMap(p => { 46 | p match { 47 | case NamedAssign(_, n, r: Reference, _, _, _, _) if(r.serialize == cm) => Some(n) 48 | case NoNameAssign(_, r: Reference, _, _, _, _, _) if(r.serialize == cm) => None // index ? 49 | case _ => None 50 | } 51 | }) match { 52 | case Seq() => 53 | warn(i, s"Cannot find connected clock `$cm` for instance ${i.name} of module ${m.name}") 54 | m // nothing to do, clock does not seem connected 55 | case Seq(n) => 56 | m match { 57 | case e: ExtModule => e.copy(clock = Some(n)) 58 | case e: Module => e.copy(clock = Some(n)) 59 | } 60 | case _ => 61 | warn(i, s"Found multiple clock `$cm` for instance ${i.name} of module ${m.name}: aborting update") 62 | m 63 | } 64 | }) 65 | m match { 66 | case _: ExtModule => i // nothing to update in portmap 67 | case _: Module => i.copy(portMap = updatedPortMap(cm), clock = Some(cm)) 68 | } 69 | 70 | case (Some(ci), None) => 71 | // update port map for instance 72 | // NOTE: updating the port map here is not enough, Module(new sub) will require withClock context 73 | val renamedClock = m.ports.collectFirst{ 74 | case port:Port if(!port.isDefaultClock.isEmpty) => port.isDefaultClock.get 75 | }.getOrElse("") 76 | val portMap = i.portMap.map(p => { 77 | p match { 78 | case na @NamedAssign(_, n, _, _, _, _, _) if(n == renamedClock) => 79 | na.copy(name = ci) 80 | case _ => p 81 | } 82 | }) 83 | i.copy(portMap = portMap, clock = Some(ci)) 84 | 85 | case _ => i // nothing to do ??? 86 | } 87 | 88 | case None => 89 | // not sure that the warning is necessary 90 | warn(i, s"Module ${i.module.serialize} referenced by instance ${i.name} cannot be found in current design. Clock & Reset management might be inaccurate.") 91 | i 92 | } 93 | } 94 | 95 | def processStatement(s: Statement): Statement = { 96 | s match { 97 | case i: DefInstance => processInstance(i) 98 | case _ => s.mapStmt(processStatement) 99 | } 100 | } 101 | 102 | module.mapStmt(processStatement) 103 | // TO DO : if one clock was infered from sub modules please update current module 104 | // and then redo the transform to ensure hierarchical propagation 105 | } 106 | } -------------------------------------------------------------------------------- /src/test/scala/AddDontCareSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | import sv2chisel.TranslationOptions 11 | 12 | class AddDontCareSpec extends Sv2ChiselSpec { 13 | Logger.setLevel(LogLevel.Warn) 14 | 15 | behavior of "AddDontCare" 16 | 17 | 18 | it should "apply on incomplete named port map for modules" in { 19 | 20 | val sub = wrapInModule(""" 21 | |input [7:0] i; 22 | |output [7:0] o; 23 | |input [7:0] extra; 24 | | 25 | |assign o = i; 26 | """.stripMargin, 27 | "my_sub" 28 | ) 29 | 30 | val main = wrapInModule(""" 31 | |input [7:0] i; 32 | |output [7:0] o; 33 | | 34 | |my_sub inst( 35 | | .i(i), 36 | | .o(o) 37 | |); 38 | """.stripMargin 39 | ) 40 | 41 | val result = emit(sub + main, Some("raw")) 42 | 43 | result should containStr ( "class my_sub() extends RawModule {") 44 | 45 | result should containStr ( 46 | "val inst = Module(new my_sub)", 47 | "inst.i := i", 48 | "o := inst.o", 49 | "inst.extra := DontCare" 50 | ) 51 | } 52 | it should "apply on incomplete named port map for blackboxes" in { 53 | 54 | val bb = wrapInModule(""" 55 | |input [7:0] i; 56 | |output [7:0] o; 57 | |input [7:0] extra; 58 | | 59 | |assign o = i; 60 | """.stripMargin, 61 | "my_black_box" 62 | ) 63 | 64 | val main = wrapInModule(""" 65 | |input [7:0] i; 66 | |output [7:0] o; 67 | | 68 | |my_black_box inst( 69 | | .i(i), 70 | | .o(o) 71 | |); 72 | """.stripMargin 73 | ) 74 | 75 | val result = emit(bb, main, Some("raw"), TranslationOptions()) 76 | 77 | result should containStr ( "class my_black_box() extends BlackBox {") 78 | 79 | result should containStr ( 80 | "val inst = Module(new my_black_box)", 81 | "inst.io.i := i", 82 | "o := inst.io.o", 83 | "inst.io.extra := DontCare" 84 | ) 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /src/test/scala/AsUIntSubRangeSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class AsUIntSubAccessSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "AsUIntSubAccess" 14 | 15 | it should "add apply for asUInt() in subrange" in { 16 | val result = emitInModule(""" 17 | |typedef struct packed { 18 | | logic bool; 19 | | logic [7:0] uint; 20 | |} tpe_t; 21 | | 22 | |wire tpe_t a; 23 | |wire [3:0] b; 24 | | 25 | |assign b = a[3:0]; 26 | """.stripMargin 27 | ) 28 | debug(result) 29 | 30 | result shouldNot containStr ( "b := a.asUInt(3,0)" ) 31 | result should containStr ( "b := a.asUInt.apply(3,0)" ) 32 | } 33 | 34 | it should "add apply for asUInt() in subindex" in { 35 | val result = emitInModule(""" 36 | |typedef struct packed { 37 | | logic bool; 38 | | logic [7:0] uint; 39 | |} tpe_t; 40 | | 41 | |wire tpe_t a; 42 | |wire b; 43 | | 44 | |assign b = a[3]; 45 | """.stripMargin 46 | ) 47 | debug(result) 48 | 49 | result shouldNot containStr ( "b := a.asUInt(3)" ) 50 | result should containStr ( "b := a.asUInt.apply(3)" ) 51 | } 52 | 53 | it should "add apply for literals in subindex" in { 54 | val result = emitInModule(""" 55 | |localparam P = 5[2]; 56 | """.stripMargin 57 | ) 58 | debug(result) 59 | 60 | result should containStr ( "val P: Bool = 5.U.apply(2)" ) 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/test/scala/BasicSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class BasicSpecs extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "BasicSpecs" 14 | it should "be properly emitted" in { 15 | val result = emitInModule(s""" 16 | |localparam A = 5; 17 | |localparam B = $$clog2(A + 8); 18 | |localparam C = A == 64 ? 1 : B; 19 | |localparam D = {"test", "0"+A}; 20 | |localparam E = 2 ** A; 21 | |localparam hw = 8'b10000000; 22 | | 23 | |genvar i, bank; 24 | |wire w, z; 25 | |wire [7:0] state; 26 | |reg r; 27 | |wire s; 28 | |assign r = A == 64 ? 1 : 0; 29 | |assign w = A == 5 ? 1 : A; 30 | |assign z = state == hw ? r : w; 31 | | 32 | |wire [5:0] a; 33 | |wire b; 34 | |wire c; 35 | |assign c = a == '1 && b; 36 | |// avoid simplification => harder ... 37 | |wire [5:0] aa; 38 | |wire d; 39 | |assign aa[1] = '0; 40 | |assign c = aa == '1 && b; 41 | | 42 | |wire [63:0] ascii; 43 | |assign ascii = "test"; 44 | | 45 | """.stripMargin 46 | ) 47 | debug(result) 48 | result should containStr ("class Test() extends RawModule {") 49 | result should containStr ("// genvar i, bank;") 50 | 51 | result should containStr ("val A = 5") 52 | result should containStr ("val B = util.log2Ceil(A+8)") 53 | result should containStr ("val C = if(A == 64) 1 else B") 54 | result should containStr ("val D = \"test\" + A") // no need for generic handling ... 55 | result should containStr ("val E = 1 << A") 56 | 57 | result should containStr ("val w = Wire(Bool())") 58 | result should containStr ("val r = Wire(Bool())") 59 | result should containStr ("val z = Wire(Bool())") 60 | 61 | // this conversion is another challenge : 62 | result should containStr ("r := Mux(A.U === 64.U, true.B, false.B)") 63 | result should containStr ("w := Mux(A.U === 5.U, true.B, (A != 0).B)") 64 | result should containStr ("z := Mux(state === hw, r, w)") 65 | 66 | result should containStr ("c := (a === 63.U) && b") 67 | result should containStr ("c := (aa.asUInt === 63.U) && b") 68 | // TO DO : leverage implicits ??? 69 | // result should containStr ("c := a === Ones && b") 70 | 71 | result should containStr ("val ascii = Wire(UInt(64.W))") 72 | result should containStr ("ascii := \"test\".V.asTypeOf(UInt(64.W))") 73 | 74 | } 75 | 76 | it should "deal with verilog literal tricks" in { 77 | val result = emitInModule(s""" 78 | |wire [31:0] test; 79 | |assign test = ~0; 80 | |assign test[5:0] = '0; 81 | |wire [31:0] test2; 82 | |assign test2 = ~0; 83 | |wire [31:0] test3; 84 | |assign test3 = ~(0+0); 85 | """.stripMargin 86 | ) 87 | debug(result) 88 | result should containStr ("class Test() extends RawModule {") 89 | result should containStr ("val test = Wire(Vec(32, Bool()))") 90 | result should containStr ("test := ( ~0.U(32.W)).asBools") 91 | result should containStr ("test(5,0) := 0.U.asTypeOf(Vec(6, Bool()))") 92 | result should containStr ("val test2 = Wire(UInt(32.W))") 93 | result should containStr ("test2 := ~0.U(32.W)") 94 | result should containStr ("val test3 = Wire(UInt(32.W))") 95 | result should containStr ("test3 := ~(0+0).U(32.W)") 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/test/scala/BundleTypeSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class BundleTypeSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "BundleType" 14 | 15 | def check(result: String) = { 16 | result should containStr ("class t1_t extends Bundle {", 17 | "val bool = Bool()", 18 | "val uint = UInt(B.W)", 19 | "val vec = Vec(B, Bool())", 20 | "val vecU = Vec(A, UInt(B.W))", 21 | "val matrix = Vec(A, Vec(B, Bool()))", 22 | "}") 23 | 24 | result should containStr ("val W = (new t1_t).getWidth") 25 | result should containStr ("class t2_t extends Bundle {", 26 | "val bool = Bool()", 27 | "val uint = UInt(W.W)", 28 | "val vec = Vec(B, new t1_t)", 29 | "val tpe = new t1_t", 30 | "}") 31 | result should containStr ("val test_wire = Wire(Vec(L, new t2_t))") 32 | result should containStr ("val test_reg = Reg(Vec(L, new t2_t))") 33 | result should containStr ("val test_raw_bundle = Wire(new t2_t)") 34 | 35 | result should containStr ("test_reg(0).bool := true.B", 36 | "test_reg(0).uint := 0.U", 37 | "test_reg(0).vec := 0.U.asTypeOf(Vec(B, new t1_t))", 38 | "test_reg(0).vec(0) := 0.U.asTypeOf(new t1_t)", 39 | "test_reg(0).vec(0).uint := 0.U", 40 | "test_reg(0).vec(0).vec(1) := false.B", 41 | "test_reg(0).vec(0).vecU(1) := 0.U", 42 | "test_reg(0).vec(0).matrix(1)(1) := false.B", 43 | "test_reg(0).tpe.uint := 0.U" 44 | ) 45 | 46 | result should containStr ("test_wire := 0.U.asTypeOf(Vec(L, new t2_t))") 47 | result should containStr ("test_wire := ((1.U << (L*(new t2_t).getWidth))-1.U).asTypeOf(Vec(L, new t2_t))") 48 | result should containStr ("test_raw_bundle := 0.U.asTypeOf(new t2_t)") 49 | } 50 | 51 | it should "be properly emitted with def within the same module" in { 52 | val result = emitInModule(s""" 53 | |input clk; 54 | | 55 | |localparam A = 5; 56 | |localparam B = $$clog2(A + 8); 57 | | 58 | |typedef struct packed { 59 | | logic bool; 60 | | logic [B-1:0] uint; 61 | | logic [B-1:0] vec; 62 | | logic [A-1:0][B-1:0] vecU; 63 | | logic [A-1:0][B-1:0] matrix; 64 | |} t1_t; 65 | |localparam W = $$bits(t1_t); 66 | | 67 | |typedef struct packed { 68 | | logic bool; 69 | | logic [W-1:0] uint; 70 | | t1_t [B-1:0] vec; 71 | | t1_t tpe; 72 | |} t2_t; 73 | | 74 | |localparam L = 5; 75 | | 76 | |t2_t [L-1:0] test_wire; 77 | |t2_t [L-1:0] test_reg; 78 | |t2_t test_raw_bundle; 79 | | 80 | |always @(posedge clk) begin 81 | | test_reg[0].bool <= '1; 82 | | test_reg[0].uint <= '0; 83 | | test_reg[0].vec <= '{default: '0}; 84 | | test_reg[0].vec[0] <= '0; 85 | | test_reg[0].vec[0].uint <= '0; 86 | | test_reg[0].vec[0].vec[1] <= '0; 87 | | test_reg[0].vec[0].vecU[1] <= '0; 88 | | test_reg[0].vec[0].matrix[1][1] <= '0; 89 | | test_reg[0].tpe.uint <= '0; 90 | |end 91 | |assign test_wire = '{default: '0}; 92 | |assign test_wire = '{default: '1}; 93 | |assign test_raw_bundle = '{default: '0}; 94 | """.stripMargin 95 | ) 96 | debug(result) 97 | result should containStr ("class Test() extends Module {") 98 | check(result) 99 | } 100 | 101 | it should "be properly emitted with def in a package" in { 102 | val pkg = wrapInPackage(s""" 103 | |localparam A = 5; 104 | |localparam B = $$clog2(A + 8); 105 | | 106 | |typedef struct packed { 107 | | logic bool; 108 | | logic [B-1:0] uint; 109 | | logic [B-1:0] vec; 110 | | logic [A-1:0][B-1:0] vecU; 111 | | logic [A-1:0][B-1:0] matrix; 112 | |} t1_t; 113 | |localparam W = $$bits(t1_t); 114 | | 115 | |typedef struct packed { 116 | | logic bool; 117 | | logic [W-1:0] uint; 118 | | t1_t [B-1:0] vec; 119 | | t1_t tpe; 120 | |} t2_t; 121 | """.stripMargin, 122 | "my_package" 123 | ) 124 | 125 | val mod = wrapInModule(s""" 126 | |input clk; 127 | | 128 | |import my_package::*; 129 | | 130 | |localparam L = 5; 131 | | 132 | |t2_t [L-1:0] test_wire; 133 | |t2_t [L-1:0] test_reg; 134 | |t2_t test_raw_bundle; 135 | | 136 | |always @(posedge clk) begin 137 | | test_reg[0].bool <= '1; 138 | | test_reg[0].uint <= '0; 139 | | test_reg[0].vec <= '{default: '0}; 140 | | test_reg[0].vec[0] <= '0; 141 | | test_reg[0].vec[0].uint <= '0; 142 | | test_reg[0].vec[0].vec[1] <= '0; 143 | | test_reg[0].vec[0].vecU[1] <= '0; 144 | | test_reg[0].vec[0].matrix[1][1] <= '0; 145 | | test_reg[0].tpe.uint <= '0; 146 | |end 147 | |assign test_wire = '{default: '0}; 148 | |assign test_wire = '{default: '1}; 149 | |assign test_raw_bundle = '{default: '0}; 150 | """.stripMargin 151 | ) 152 | val result = emit(pkg + mod) 153 | debug(result) 154 | result should containStr ("package object my_package {") 155 | result should containStr ("class Test() extends Module {") 156 | check(result) 157 | } 158 | 159 | } -------------------------------------------------------------------------------- /src/test/scala/ComplexRangeSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class ComplexRangeSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | "ComplexRangeSpec" should "be properly emitted" in { 14 | val result = emitInModule(s""" 15 | |logic [31: 0] a_vect; 16 | |logic [0 :31] b_vect; 17 | |logic [7 :0] a_res; 18 | |logic [0 :7] b_res; 19 | | 20 | |logic [63: 0] dword; 21 | |logic [7: 0] d_res; 22 | |logic [7: 0] e_res; 23 | |logic [7: 0] f_res; 24 | |integer sel; 25 | | 26 | |assign a_res = a_vect[ 0 +: 8]; // == a_vect[ 7 : 0] 27 | |assign a_res = a_vect[15 -: 8]; // == a_vect[15 : 8] 28 | |assign b_res = b_vect[ 0 +: 8]; // == b_vect[0 : 7] 29 | |assign b_res = b_vect[15 -: 8]; // == b_vect[8 :15] 30 | | 31 | |// variable part-select with fixed width 32 | |assign d_res = dword[8*sel +: 8]; 33 | |localparam W = 8; 34 | |assign e_res = dword[W*sel -: W]; 35 | |localparam P = 7; 36 | |assign f_res = dword[W*P -: W]; 37 | | 38 | """.stripMargin 39 | ) 40 | debug(result) 41 | result should containStr ("class Test() extends RawModule {") 42 | result should containStr ("val a_vect = Wire(Vec(32, Bool()))") 43 | result should containStr ("val b_vect = Wire(Vec(32, Bool()))") 44 | result should containStr ("val a_res = Wire(UInt(8.W))") 45 | result should containStr ("val b_res = Wire(UInt(8.W))") 46 | result should containStr ("val dword = Wire(Vec(64, Bool()))") 47 | result should containStr ("val d_res = Wire(UInt(8.W))") 48 | result should containStr ("val sel = Wire(UInt(64.W))") 49 | result should containStr ("a_res := a_vect(7,0).asUInt // == a_vect[ 7 : 0]") 50 | result should containStr ("a_res := a_vect(15,8).asTypeOf(UInt(8.W)) // == a_vect[15 : 8]") 51 | result should containStr ("b_res := b_vect(7,0).asUInt // == b_vect[0 : 7]") 52 | result should containStr ("b_res := b_vect(15,8).asTypeOf(UInt(8.W)) // == b_vect[8 :15]") 53 | result should containStr ("d_res := dword((8.U*sel)+7.U,8.U*sel).asTypeOf(UInt(8.W))") 54 | result should containStr ("e_res := dword(W.U*sel,(W.U*sel)-(W-1).U).asTypeOf(UInt(8.W))") 55 | result should containStr ("f_res := dword(W*P,(W*P)-(W-1)).asTypeOf(UInt(8.W))") 56 | 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/test/scala/DefLogicSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class DefLogicSpecs extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "InferDefLogic" 14 | 15 | it should "emit RegInit properly" in { 16 | val result = emitInModule(s""" 17 | |localparam WWW = 1; 18 | |localparam WW = 1; 19 | | 20 | |wire clk; 21 | |wire change; 22 | |reg [WW-1:0] counter = '0; 23 | |reg [WWW-1:0] current = '1; 24 | | 25 | |always @(posedge clk) begin 26 | | if (change) begin 27 | | if (current < (1< false.B)).asUInt, a(WB-1,0).asUInt))", 41 | "r := Mux(c, a(WA-1,0).asUInt, Cat((VecInit.tabulate(PADDING)(_ => true.B)).asUInt, a(WB-1,0).asUInt))" 42 | ) 43 | 44 | // auto 45 | result should containStr ( 46 | "r := Mux(c, a(WA-1,0).asUInt, Cat(0.U((WA-WB).W), a(WB-1,0).asUInt))", 47 | "r := Mux(c, a(WA-1,0).asUInt, Cat(((1.U << (WA-WB))-1.U).asUInt, a(WB-1,0).asUInt))", 48 | ) 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/test/scala/RemoveConcatSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class RemoveConcatSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "RemoveConcats" 14 | 15 | it should "properly remove concats or use Cat" in { 16 | val result = emitInModule(s""" 17 | |// might containStr more than the RAM due to moves 18 | |localparam DBLW = 2; 19 | |localparam WWW = 16; 20 | | 21 | |wire bool; 22 | |wire [DBLW-1:0][WWW:0] pack; 23 | |wire [DBLW-1:0][WWW-1:0] packsmall; 24 | | 25 | |generate 26 | | for (i = 0; i < DBLW/2; i++) begin: loop 27 | | assign {bool, packsmall[i][WWW-1:1], packsmall[i][0]} = pack[2*i]; 28 | | assign pack[2*i+1] = bool ? {packsmall[i][WWW-1:0], 1'b0} : '0; 29 | | end 30 | |endgenerate 31 | """.stripMargin 32 | ) 33 | debug(result) 34 | result should containStr ("class Test() extends RawModule {") 35 | result should containStr ("val auto_concat = Wire(new Bundle {", 36 | "val bool = Bool()", 37 | "val packsmall_i_WWW_1_1 = Vec(WWW-1, Bool())", 38 | "val packsmall_i_0 = Bool()", 39 | "})", 40 | "auto_concat := pack(2*i).asTypeOf(auto_concat)", 41 | "bool := auto_concat.bool", 42 | "packsmall(i)(WWW-1,1) := auto_concat.packsmall_i_WWW_1_1", 43 | "packsmall(i)(0) := auto_concat.packsmall_i_0") 44 | 45 | // seccond concat inline 46 | result should containStr ("pack((2*i)+1) := Mux(bool, Cat(packsmall(i)(WWW-1,0).asUInt, \"b0\".U(1.W)), 0.U)") 47 | 48 | } 49 | 50 | it should "properly emit Cat with subrange and replicate patterns" in { 51 | val result = emitInModule(s""" 52 | |localparam WWW = 16; 53 | |localparam AAA = 4; 54 | | 55 | |wire [WWW-1:0] w; 56 | |wire [WWW-1:0] y; 57 | | 58 | |assign w = {{(WWW-AAA){1'b0}}, y[AAA-1:0]}; 59 | """.stripMargin 60 | ) 61 | debug(result) 62 | result should containStr ("import chisel3._") 63 | result should containStr ("import sv2chisel.helpers.vecconvert._") 64 | result should containStr ("import chisel3.util.Cat") 65 | 66 | result should containStr ("class Test() extends RawModule {") 67 | result should containStr ( 68 | "val w = Wire(UInt(WWW.W))", 69 | "val y = Wire(Vec(WWW, Bool()))", 70 | "w := Cat((VecInit.tabulate((WWW-AAA))(_ => false.B)).asUInt, y(AAA-1,0).asUInt)", 71 | ) 72 | 73 | } 74 | 75 | it should "properly emit Cat with subrange and replicate patterns in functions" in { 76 | val result = emit(wrapInPackage(s""" 77 | |localparam WWW = 16; 78 | |localparam AAA = 4; 79 | |function [WWW-1:0] w; 80 | | input logic [WWW-1:0] y; 81 | | w = {{(WWW-AAA){1'b0}}, y[AAA-1:0]}; 82 | |endfunction; 83 | """.stripMargin 84 | )) 85 | debug(result) 86 | result should containStr ("import chisel3._") 87 | result should containStr ("import sv2chisel.helpers.vecconvert._") 88 | result should containStr ("import chisel3.util.Cat") 89 | 90 | result should containStr ( 91 | "def w(y:Vec[Bool]): UInt = {", 92 | "Cat((VecInit.tabulate((WWW-AAA))(_ => false.B)).asUInt, y(AAA-1,0).asUInt)", 93 | "}" 94 | ) 95 | 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/test/scala/StringSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class StringSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | behavior of "String" 14 | 15 | it should "be casted properly" in { 16 | val result = emit(wrapInPackage(""" 17 | |localparam W = 64; 18 | |function logic check_str; 19 | | // str is 8 bytes / 64 bits 20 | | input logic[W-1:0] str; 21 | | // first check against 8 chars second check against only 4 chars (32 bits) 22 | | check_str = str[W-1:0] == "OPTIONS " || str[W-1:32] == "GET "; 23 | |endfunction 24 | """.stripMargin 25 | )) 26 | debug(result) 27 | 28 | result should containStr ( "import sv2chisel.helpers.vecconvert._" ) // to get .V 29 | 30 | // probably the most decent option (asUInt) 31 | result should containStr ( "(str(W-1,0).asUInt === \"OPTIONS \".V.asUInt) || (str(W-1,32).asUInt === \"GET \".V.asUInt)" ) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/test/scala/SwitchSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | class SwitchSpec extends Sv2ChiselSpec { 11 | Logger.setLevel(LogLevel.Warn) 12 | 13 | /* 14 | 15 | TODO: refacto type management with traits 16 | 17 | - main question being : shall we totally decouple hw types from sw types ? 18 | 19 | - one trait TypeKind 20 | - HwTypeKind 21 | - SwTypeKind 22 | - MixedTypeKind ? 23 | 24 | - one trait TypeClass 25 | - BoolTypeClass 26 | - NumTypeClass 27 | - DataTypeClass (hw on which you can do asUInt / asTypeOf) 28 | - 29 | - there can be numeric sw and numeric hw types (or numeric mixed ?) 30 | 31 | - how to have a function that can passed a trait as argument to now whether to cast or not 32 | - for example a conditionally requires a BoolType 33 | 34 | 35 | 36 | */ 37 | 38 | "SwitchSpec" should "be properly emitted" in { 39 | val result = emitInModule(""" 40 | |input clk; 41 | |wire a, b, c, d, e; 42 | |wire [31:0] mem, prev; 43 | |reg [31:0] res; 44 | | 45 | |always @(posedge clk) begin 46 | | case (1'b1) 47 | | a: 48 | | res <= prev; 49 | | |{b, c}: 50 | | res <= mem[31:12] << 12; 51 | | |{a, d, e}: 52 | | res <= $signed(mem[31:20]); 53 | | &{a, b}: 54 | | res <= $signed({mem[31], mem[7], mem[30:25], mem[11:8], 1'b0}); 55 | | b: 56 | | res <= $signed({mem[31:25], mem[11:7]}); 57 | | default: 58 | | res <= 1'bx; 59 | | endcase 60 | |end 61 | """.stripMargin 62 | ) 63 | debug(result) 64 | result should containStr ("class Test() extends Module {") 65 | 66 | result should containStr ("val mem = Wire(Vec(32, Bool()))", 67 | "val prev = Wire(UInt(32.W))", 68 | "val res = Reg(UInt(32.W))") 69 | 70 | result should containStr ("when(true.B === a) {", 71 | "res := prev", 72 | "} .elsewhen (true.B === Cat(b, c).orR()) {", 73 | "res := mem(31,12).asUInt << 12", 74 | "} .elsewhen (true.B === Cat(a, d, e).orR()) {", 75 | "res := mem(31,20).asTypeOf(SInt(32.W)).asUInt", 76 | "} .elsewhen (true.B === Cat(a, b).andR()) {", 77 | "res := Cat(mem(31), mem(7), mem(30,25).asUInt, mem(11,8).asUInt, \"b0\".U(1.W)).asTypeOf(SInt(32.W)).asUInt", 78 | "} .elsewhen (true.B === b) {", 79 | "res := Cat(mem(31,25).asUInt, mem(11,7).asUInt).asTypeOf(SInt(32.W)).asUInt", 80 | "} .otherwise {", 81 | "res := \"b0\".U(1.W)", 82 | "}") 83 | 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /src/test/scala/UnpackedEmissionStyleSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import sv2chisel.{TranslationOptions, ChiselizerOptions} 9 | import logger._ 10 | 11 | class UnpackedEmissionStyleSpec extends Sv2ChiselSpec { 12 | Logger.setLevel(LogLevel.Warn) 13 | 14 | // Basic Tests of Module wraper for tests 15 | behavior of "UnpackedEmissionStyle" 16 | 17 | val baseContent = wrapInModule( 18 | s""" 19 | |//Verilog wire 20 | |wire [1:0] w[3:0]; 21 | |assign w[3] = '0; 22 | |assign w[2] = '1; 23 | |assign w[1] = '1; 24 | |assign w[0] = '0; 25 | |// verilog register 26 | |input clk; 27 | |reg [1:0] r[3:0]; 28 | |always @(posedge clk) begin 29 | | r[3] <= '0; 30 | | r[2] <= '1; 31 | | r[1] <= '1; 32 | | r[0] <= '0; 33 | |end 34 | """.stripMargin 35 | ) 36 | 37 | it should "support unpacked wire & reg with UnpackedEmissionStyle.Reg" in { 38 | val chiselizerOpts = ChiselizerOptions().copy( 39 | unpackedEmissionStyle = ChiselizerOptions.UnpackedEmissionStyle.Reg 40 | ) 41 | val result = emit(baseContent, 42 | options = TranslationOptions(chiselizer = chiselizerOpts) 43 | ) 44 | debug(result) 45 | result should containStr ( 46 | "val w = Wire(Vec(4, UInt(2.W)))", 47 | "w(3) := 0.U", 48 | "w(2) := 3.U", 49 | "w(1) := 3.U", 50 | "w(0) := 0.U" 51 | ) 52 | result should containStr ( 53 | "val r = Reg(Vec(4, UInt(2.W)))", 54 | "r(3) := 0.U", 55 | "r(2) := 3.U", 56 | "r(1) := 3.U", 57 | "r(0) := 0.U", 58 | ) 59 | } 60 | 61 | it should "support unpacked wire & reg with UnpackedEmissionStyle.Mem" in { 62 | val chiselizerOpts = ChiselizerOptions().copy( 63 | unpackedEmissionStyle = ChiselizerOptions.UnpackedEmissionStyle.Mem 64 | ) 65 | val result = emit(baseContent, 66 | options = TranslationOptions(chiselizer = chiselizerOpts) 67 | ) 68 | debug(result) 69 | result should containStr ( 70 | "val w = Wire(Vec(4, UInt(2.W)))", 71 | "w(3) := 0.U", 72 | "w(2) := 3.U", 73 | "w(1) := 3.U", 74 | "w(0) := 0.U" 75 | ) 76 | result should containStr ( 77 | "val r = Mem(4,UInt(2.W))", 78 | "r(3) := 0.U", 79 | "r(2) := 3.U", 80 | "r(1) := 3.U", 81 | "r(0) := 0.U", 82 | ) 83 | } 84 | } -------------------------------------------------------------------------------- /src/test/scala/WireOrRegSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests 6 | 7 | import sv2chiselTests.utils._ 8 | import logger._ 9 | 10 | import java.io.{PrintStream, ByteArrayOutputStream} 11 | 12 | class WireOrRegSpec extends Sv2ChiselSpec { 13 | Logger.setLevel(LogLevel.Warn) 14 | 15 | behavior of "WireOrReg" 16 | 17 | it should "infer simple conditional logic " in { 18 | val result = emitInModule(""" 19 | |localparam [0:0] A = 1; 20 | |reg c; 21 | |generate 22 | | if(A) begin 23 | | assign c = '1; 24 | | end else begin 25 | | always @(posedge clk) begin 26 | | c <= '0; 27 | | end 28 | | end 29 | |endgenerate 30 | """.stripMargin 31 | ) 32 | 33 | result should containStr ("class Test() extends Module {") 34 | result should containLineSet ( 35 | " val c = if (A) { Wire(Bool()) }", 36 | " else { Reg(Bool()) }", 37 | " if(A) {", 38 | " c := true.B", 39 | " } else {", 40 | " c := false.B", 41 | " }" 42 | ) 43 | } 44 | 45 | it should "be aware of partial combinational conditional logic " in { 46 | val out = new ByteArrayOutputStream() 47 | Logger.setOutput(new PrintStream(out)) 48 | val result = emitInModule(""" 49 | |localparam [0:0] A = 1; 50 | |wire c; 51 | |generate 52 | | if(A) begin 53 | | assign c = '1; 54 | | end 55 | |endgenerate 56 | """.stripMargin 57 | ) 58 | 59 | val stdout = out.toString 60 | Logger.reset() 61 | stdout should containStr ( "[critical] Unable to guarantee complete scope definition for logic c resolved as wire without user-provided default value => adding default 0.U.asTypeOf(Bool) & ignoring A condition at sv2chiselTests.WireOrRegSpec:5:4->10" ) 62 | 63 | result should containStr ("class Test() extends RawModule {") // no clock 64 | result should containLineSet ( 65 | " val c = WireDefault(Bool(), false.B) ", 66 | " if(A) {", 67 | " c := true.B", 68 | " }" 69 | ) 70 | 71 | } 72 | 73 | it should "chill with partial clocked conditional logic " in { 74 | val result = emitInModule(""" 75 | |localparam [0:0] A = 1; 76 | |reg c; 77 | |generate 78 | | if(A) begin 79 | | always @(posedge clk) begin 80 | | c <= '0; 81 | | end 82 | | end 83 | |endgenerate 84 | """.stripMargin 85 | ) 86 | 87 | result should containStr ("class Test() extends Module {") 88 | result should containLineSet ( 89 | " val c = Reg(Bool()) ", 90 | " if(A) {", 91 | " c := false.B", 92 | " }" 93 | ) 94 | } 95 | 96 | it should "infer simple conditional logic with default" in { 97 | val result = emitInModule(""" 98 | |localparam [0:0] A = 1; 99 | |reg c = '0; 100 | |generate 101 | | if(A) begin 102 | | assign c = '1; 103 | | end else begin 104 | | always @(posedge clk) begin 105 | | c <= '0; 106 | | end 107 | | end 108 | |endgenerate 109 | """.stripMargin 110 | ) 111 | 112 | result should containStr ("class Test() extends Module {") 113 | result should containLineSet ( 114 | " val c = if (A) { WireDefault(Bool(), false.B) }", 115 | " else { RegInit(Bool(), false.B) }", 116 | " if(A) {", 117 | " c := true.B", 118 | " } else {", 119 | " c := false.B", 120 | " }" 121 | ) 122 | } 123 | 124 | 125 | it should "infer more complex conditional logic " in { 126 | val result = emitInModule(""" 127 | |localparam [0:0] A, B = 1; 128 | |reg c; 129 | |generate 130 | | if(A) begin 131 | | assign c = '1; 132 | | end else if(B) begin 133 | | always @(posedge clk) begin 134 | | if( c == '0) begin 135 | | c <= '1; 136 | | end else begin 137 | | c <= '0; 138 | | end 139 | | end 140 | | end else begin 141 | | assign c = '0; 142 | | end 143 | |endgenerate 144 | """.stripMargin 145 | ) 146 | 147 | result should containStr ("class Test() extends Module {") 148 | result should containLineSet ( 149 | " val c = if (A || (( !A) && ( !B))) { Wire(Bool()) }", 150 | " else if (( !A) && B) { Reg(Bool()) }", 151 | " if(A) {", 152 | " c := true.B", 153 | " } else if(B) {", 154 | " when(c === false.B) {", 155 | " c := true.B", 156 | " } .otherwise {", 157 | " c := false.B", 158 | " }", 159 | " } else {", 160 | " c := false.B", 161 | " }" 162 | ) 163 | 164 | } 165 | } -------------------------------------------------------------------------------- /src/test/scala/utils/ChiselMatchers.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests.utils 6 | 7 | import org.scalatest._ 8 | import matchers.should._ 9 | import collection.mutable.ArrayBuffer 10 | 11 | trait ChiselMatchers extends Matchers { 12 | val ln = sys.props("line.separator") 13 | val dq = "\"" 14 | import matchers._ 15 | /** Checks that the emitted circuit has the expected lines in order */ 16 | def containLineSet(expectedLines: String*) = new ChiselStrictStringsMatcher(expectedLines) 17 | def containStr(expectedLines: String*) = new ChiselFlexStringsMatcher(expectedLines) 18 | 19 | def findFaillingLine(data: Array[String], expected: Seq[String]): String = { 20 | val msg = ArrayBuffer[String]() 21 | val starts = data.zipWithIndex.collect { case (s, t) if (s == expected.head) => t } 22 | if(starts.isEmpty) msg += s"[DEBUG] Unable to find a first matching line (${expected.head})" 23 | starts.foreach(i => { 24 | msg += s"DEBUG: starting at index $i" 25 | data.drop(i).zip(expected).foreach { 26 | case (chisel, exp) if (chisel != exp ) => msg += s"[DEBUG]: failing at `$chisel` (expected: `$exp`)" 27 | case _ => 28 | } 29 | }) 30 | msg.mkString(ln) 31 | } 32 | 33 | class ChiselStrictStringsMatcher(expectedLines: Seq[String]) extends Matcher[String] { 34 | override def apply(chisel: String): MatchResult = { 35 | val data = chisel.split(ln) 36 | MatchResult( 37 | data.containsSlice(expectedLines), 38 | chisel + s"$ln did not contain $dq" + expectedLines + dq + ln +s"${ln}Details:${ln}" + findFaillingLine(data, expectedLines), 39 | s"Emitted chisel contained $expectedLines" 40 | ) 41 | } 42 | } 43 | class ChiselFlexStringsMatcher(expectedLines: Seq[String]) extends Matcher[String] { 44 | override def apply(chisel: String): MatchResult = { 45 | // remove leading & trailling spaces + console color tags 46 | val data = chisel.split(ln).map(_.trim.replaceAll("\\P{Print}\\[[0-9]+m", "")) 47 | MatchResult( 48 | data.containsSlice(expectedLines), 49 | chisel + s"$ln did not contain $dq" + expectedLines + dq + ln +s"${ln}Details:${ln}" + findFaillingLine(data, expectedLines), 50 | s"Emitted chisel contained $expectedLines" 51 | ) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/scala/utils/Sv2ChiselSpec.scala: -------------------------------------------------------------------------------- 1 | // Use of this source code is governed by a BSD-style 2 | // license that can be found in the LICENSE file. 3 | // Copyright 2020 The sv2chisel Authors. All rights reserved. 4 | 5 | package sv2chiselTests.utils 6 | 7 | import sv2chisel._ 8 | import org.scalatest._ 9 | import flatspec._ 10 | 11 | import logger._ 12 | 13 | abstract class Sv2ChiselSpec extends AnyFlatSpec with ChiselMatchers with EasyLogging { 14 | def defaultPath(path: Option[String]): Some[String] = path match { 15 | case Some(p) => Some(p) 16 | case None => Some(this.getClass.getName) 17 | } 18 | 19 | def emit(input: String, path: Option[String] = None, options: TranslationOptions = TranslationOptions()): String = 20 | Driver.emitChisel(Project("test", input, defaultPath(path)), options, noFileIO = true) 21 | 22 | def emit(blackboxes: String, main: String, path: Option[String], options: TranslationOptions): String = 23 | Driver.emitChisel(Project("test", blackboxes, main, defaultPath(path)), options, noFileIO = true) 24 | 25 | // to do add optional param & ports 26 | def wrapInModule(body: String, name : String = "Test") : String = { 27 | s""" 28 | |module $name(); 29 | |""".stripMargin ++ 30 | body.split("\n").mkString(" ", "\n ", "") ++ """ 31 | |endmodule 32 | |""".stripMargin 33 | } 34 | 35 | def wrapInPackage(body: String, name : String = "Test") : String = { 36 | s""" 37 | |package $name; 38 | |""".stripMargin ++ 39 | body.split("\n").mkString(" ", "\n ", "") ++ """ 40 | |endpackage 41 | |""".stripMargin 42 | } 43 | 44 | def emitInModule(body: String) : String = { 45 | val str = wrapInModule(body) 46 | debug(str) 47 | emit(str) 48 | } 49 | 50 | def emitInModule(name: String, body : String) : String = { 51 | val str = wrapInModule(body, name) 52 | debug(str) 53 | emit(str) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /utils/bin/sv2chisel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path=`dirname "$0"` 4 | cmd="java -cp ${path}/sv2chisel.jar sv2chisel.Main ${@:1}" 5 | eval $cmd -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / version := "0.5.1-SNAPSHOT" 2 | --------------------------------------------------------------------------------