From 6d2708871dd2bca826ac7b39756b615cd8c668ce Mon Sep 17 00:00:00 2001 From: oronpo Date: Wed, 15 May 2024 00:29:13 +0300 Subject: [PATCH 1/8] trapping exit in tests and preventing sbt shutdown on exit --- build.sbt | 4 +++- .../src/test/scala/dfhdl/ExitTrapSuite.scala | 15 +++++++++++++++ lib/src/test/scala/issues/IssueSpec.scala | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 internals/src/test/scala/dfhdl/ExitTrapSuite.scala diff --git a/build.sbt b/build.sbt index b88b1a444..666d8e95c 100755 --- a/build.sbt +++ b/build.sbt @@ -107,6 +107,7 @@ lazy val lib = project ) .dependsOn( core % "test->test;compile->compile", + internals % "test->test;compile->compile", compiler_stages ) @@ -174,5 +175,6 @@ lazy val pluginTestUseSettings = Seq( ) lazy val commonSettings = Seq( - scalacOptions ++= compilerOptions + scalacOptions ++= compilerOptions, + Test / run / fork := true ) diff --git a/internals/src/test/scala/dfhdl/ExitTrapSuite.scala b/internals/src/test/scala/dfhdl/ExitTrapSuite.scala new file mode 100644 index 000000000..f98e7c21a --- /dev/null +++ b/internals/src/test/scala/dfhdl/ExitTrapSuite.scala @@ -0,0 +1,15 @@ +package dfhdl + +class ExitTrapSuite extends munit.FunSuite: + sealed case class ExitException(status: Int) + extends SecurityException(s"sys.exit($status) event discovered") {} + sealed class NoExitSecurityManager extends SecurityManager: + override def checkPermission(perm: java.security.Permission): Unit = {} + override def checkPermission(perm: java.security.Permission, context: Object): Unit = {} + override def checkExit(status: Int): Unit = + super.checkExit(status) + throw ExitException(status) + + override def beforeAll(): Unit = System.setSecurityManager(new NoExitSecurityManager()) + override def afterAll(): Unit = System.setSecurityManager(null) +end ExitTrapSuite diff --git a/lib/src/test/scala/issues/IssueSpec.scala b/lib/src/test/scala/issues/IssueSpec.scala index 91aabf249..373292de0 100644 --- a/lib/src/test/scala/issues/IssueSpec.scala +++ b/lib/src/test/scala/issues/IssueSpec.scala @@ -4,7 +4,7 @@ import munit.* import dfhdl.* import dfhdl.compiler.stages.getCompiledCodeString -class IssuesSpec extends FunSuite: +class IssuesSpec extends ExitTrapSuite: test("i116 compiles with no exception"): i116.GlobCounter(64).compile test("i118 compiles and passes VHDL linting"): From 5999d362112ae8804e799c6086995bb260995a91 Mon Sep 17 00:00:00 2001 From: oronpo Date: Wed, 15 May 2024 01:21:37 +0300 Subject: [PATCH 2/8] Revert "trapping exit in tests and preventing sbt shutdown on exit" This reverts commit 6d2708871dd2bca826ac7b39756b615cd8c668ce. --- build.sbt | 4 +--- .../src/test/scala/dfhdl/ExitTrapSuite.scala | 15 --------------- lib/src/test/scala/issues/IssueSpec.scala | 2 +- 3 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 internals/src/test/scala/dfhdl/ExitTrapSuite.scala diff --git a/build.sbt b/build.sbt index 666d8e95c..b88b1a444 100755 --- a/build.sbt +++ b/build.sbt @@ -107,7 +107,6 @@ lazy val lib = project ) .dependsOn( core % "test->test;compile->compile", - internals % "test->test;compile->compile", compiler_stages ) @@ -175,6 +174,5 @@ lazy val pluginTestUseSettings = Seq( ) lazy val commonSettings = Seq( - scalacOptions ++= compilerOptions, - Test / run / fork := true + scalacOptions ++= compilerOptions ) diff --git a/internals/src/test/scala/dfhdl/ExitTrapSuite.scala b/internals/src/test/scala/dfhdl/ExitTrapSuite.scala deleted file mode 100644 index f98e7c21a..000000000 --- a/internals/src/test/scala/dfhdl/ExitTrapSuite.scala +++ /dev/null @@ -1,15 +0,0 @@ -package dfhdl - -class ExitTrapSuite extends munit.FunSuite: - sealed case class ExitException(status: Int) - extends SecurityException(s"sys.exit($status) event discovered") {} - sealed class NoExitSecurityManager extends SecurityManager: - override def checkPermission(perm: java.security.Permission): Unit = {} - override def checkPermission(perm: java.security.Permission, context: Object): Unit = {} - override def checkExit(status: Int): Unit = - super.checkExit(status) - throw ExitException(status) - - override def beforeAll(): Unit = System.setSecurityManager(new NoExitSecurityManager()) - override def afterAll(): Unit = System.setSecurityManager(null) -end ExitTrapSuite diff --git a/lib/src/test/scala/issues/IssueSpec.scala b/lib/src/test/scala/issues/IssueSpec.scala index 373292de0..91aabf249 100644 --- a/lib/src/test/scala/issues/IssueSpec.scala +++ b/lib/src/test/scala/issues/IssueSpec.scala @@ -4,7 +4,7 @@ import munit.* import dfhdl.* import dfhdl.compiler.stages.getCompiledCodeString -class IssuesSpec extends ExitTrapSuite: +class IssuesSpec extends FunSuite: test("i116 compiles with no exception"): i116.GlobCounter(64).compile test("i118 compiles and passes VHDL linting"): From fc396e7461a60fd78b9bb2250a0f50bd78c23998 Mon Sep 17 00:00:00 2001 From: oronpo Date: Thu, 16 May 2024 13:26:02 +0300 Subject: [PATCH 3/8] revamped tool options --- .../main/scala/dfhdl/options/OnError.scala | 6 ++++ lib/src/main/scala/dfhdl/default.scala | 4 +-- .../scala/dfhdl/options/BuilderOptions.scala | 14 ++++++++ .../scala/dfhdl/options/GHDLOptions.scala | 27 ++++++++++++++ .../scala/dfhdl/options/LinterOptions.scala | 20 +++++++++++ .../scala/dfhdl/options/ToolOptions.scala | 13 +++++++ .../dfhdl/options/VerilatorOptions.scala | 30 ++++++++++++++++ .../scala/dfhdl/tools/AvailableTools.scala | 4 +-- .../scala/dfhdl/tools/toolsCore/GHDL.scala | 9 +++-- .../scala/dfhdl/tools/toolsCore/Tool.scala | 35 ++++++++++++++----- .../dfhdl/tools/toolsCore/Verilator.scala | 7 ++-- .../scala/dfhdl/tools/toolsCore/Vivado.scala | 6 +++- .../scala/dfhdl/tools/toolsCore/helpers.scala | 6 ++++ 13 files changed, 164 insertions(+), 17 deletions(-) create mode 100644 core/src/main/scala/dfhdl/options/OnError.scala create mode 100644 lib/src/main/scala/dfhdl/options/BuilderOptions.scala create mode 100644 lib/src/main/scala/dfhdl/options/GHDLOptions.scala create mode 100644 lib/src/main/scala/dfhdl/options/LinterOptions.scala create mode 100644 lib/src/main/scala/dfhdl/options/ToolOptions.scala create mode 100644 lib/src/main/scala/dfhdl/options/VerilatorOptions.scala create mode 100644 lib/src/main/scala/dfhdl/tools/toolsCore/helpers.scala diff --git a/core/src/main/scala/dfhdl/options/OnError.scala b/core/src/main/scala/dfhdl/options/OnError.scala new file mode 100644 index 000000000..2bab7a05e --- /dev/null +++ b/core/src/main/scala/dfhdl/options/OnError.scala @@ -0,0 +1,6 @@ +package dfhdl.options + +enum OnError derives CanEqual: + case Exit, Exception +object OnError: + given OnError = OnError.Exit diff --git a/lib/src/main/scala/dfhdl/default.scala b/lib/src/main/scala/dfhdl/default.scala index 9905a674a..4b09d754c 100644 --- a/lib/src/main/scala/dfhdl/default.scala +++ b/lib/src/main/scala/dfhdl/default.scala @@ -11,9 +11,9 @@ extension [D <: Design](cd: CompiledDesign[D]) verilogLinter: VerilogLinter, vhdlLinter: VHDLLinter, co: CompilerOptions - ): CompiledDesign[D] = + )(using verilogLinter.LO, vhdlLinter.LO): CompiledDesign[D] = co.backend match case _: backends.verilog => verilogLinter.lint(verilogLinter.preprocess(cd)) case _: backends.vhdl => vhdlLinter.lint(vhdlLinter.preprocess(cd)) - def build(using builder: Builder, co: CompilerOptions): CompiledDesign[D] = + def build(using builder: Builder)(using CompilerOptions, builder.BO): CompiledDesign[D] = builder.build(builder.preprocess(cd)) diff --git a/lib/src/main/scala/dfhdl/options/BuilderOptions.scala b/lib/src/main/scala/dfhdl/options/BuilderOptions.scala new file mode 100644 index 000000000..1515859ce --- /dev/null +++ b/lib/src/main/scala/dfhdl/options/BuilderOptions.scala @@ -0,0 +1,14 @@ +package dfhdl.options + +import BuilderOptions.* + +trait BuilderOptions extends ToolOptions: + val onError: OnError + +//defaults common for all linting tools +object BuilderOptions: + opaque type OnError <: dfhdl.options.ToolOptions.OnError = dfhdl.options.ToolOptions.OnError + given Conversion[dfhdl.options.OnError, OnError] = x => x.asInstanceOf[OnError] + object OnError: + given (using onError: dfhdl.options.ToolOptions.OnError): OnError = onError + export dfhdl.options.OnError.* diff --git a/lib/src/main/scala/dfhdl/options/GHDLOptions.scala b/lib/src/main/scala/dfhdl/options/GHDLOptions.scala new file mode 100644 index 000000000..8895cdf1a --- /dev/null +++ b/lib/src/main/scala/dfhdl/options/GHDLOptions.scala @@ -0,0 +1,27 @@ +package dfhdl.options + +import GHDLOptions.* +import dfhdl.tools.toolsCore.VHDLLinterOptions + +final case class GHDLOptions( + onError: OnError, + warnAsError: WarnAsError +) extends VHDLLinterOptions + +object GHDLOptions: + given default(using + onError: OnError, + warnAsError: WarnAsError + ): GHDLOptions = GHDLOptions(onError = onError, warnAsError = warnAsError) + + opaque type OnError <: LinterOptions.OnError = LinterOptions.OnError + given Conversion[dfhdl.options.OnError, OnError] = x => x.asInstanceOf[OnError] + object OnError: + given (using onError: LinterOptions.OnError): OnError = onError + export dfhdl.options.OnError.* + + opaque type WarnAsError <: LinterOptions.WarnAsError = LinterOptions.WarnAsError + given Conversion[LinterOptions.WarnAsError, WarnAsError] = identity + object WarnAsError: + given (using warnAsError: LinterOptions.WarnAsError): WarnAsError = warnAsError +end GHDLOptions diff --git a/lib/src/main/scala/dfhdl/options/LinterOptions.scala b/lib/src/main/scala/dfhdl/options/LinterOptions.scala new file mode 100644 index 000000000..ef130b8f4 --- /dev/null +++ b/lib/src/main/scala/dfhdl/options/LinterOptions.scala @@ -0,0 +1,20 @@ +package dfhdl.options + +import LinterOptions.* + +trait LinterOptions extends ToolOptions: + val onError: OnError + val warnAsError: WarnAsError + +//defaults common for all linting tools +object LinterOptions: + opaque type OnError <: dfhdl.options.ToolOptions.OnError = dfhdl.options.ToolOptions.OnError + given Conversion[dfhdl.options.OnError, OnError] = x => x.asInstanceOf[OnError] + object OnError: + given (using onError: dfhdl.options.ToolOptions.OnError): OnError = onError + export dfhdl.options.OnError.* + + opaque type WarnAsError <: Boolean = Boolean + given Conversion[Boolean, WarnAsError] = identity + object WarnAsError: + given WarnAsError = false diff --git a/lib/src/main/scala/dfhdl/options/ToolOptions.scala b/lib/src/main/scala/dfhdl/options/ToolOptions.scala new file mode 100644 index 000000000..da6ec46f3 --- /dev/null +++ b/lib/src/main/scala/dfhdl/options/ToolOptions.scala @@ -0,0 +1,13 @@ +package dfhdl.options + +trait ToolOptions: + val onError: ToolOptions.OnError + +//defaults common for all tools +object ToolOptions: + opaque type OnError <: dfhdl.options.OnError = dfhdl.options.OnError + given Conversion[dfhdl.options.OnError, OnError] = identity + object OnError: + given (using onError: dfhdl.options.OnError): OnError = onError + export dfhdl.options.OnError.* +end ToolOptions diff --git a/lib/src/main/scala/dfhdl/options/VerilatorOptions.scala b/lib/src/main/scala/dfhdl/options/VerilatorOptions.scala new file mode 100644 index 000000000..499a88161 --- /dev/null +++ b/lib/src/main/scala/dfhdl/options/VerilatorOptions.scala @@ -0,0 +1,30 @@ +package dfhdl.options + +import VerilatorOptions.* +import dfhdl.tools.toolsCore.VerilogLinterOptions + +final case class VerilatorOptions( + onError: OnError, + warnAsError: WarnAsError +) extends VerilogLinterOptions + +object VerilatorOptions: + given default(using + onError: OnError, + warnAsError: WarnAsError + ): VerilatorOptions = VerilatorOptions( + onError = onError, + warnAsError = warnAsError + ) + + opaque type OnError <: LinterOptions.OnError = LinterOptions.OnError + given Conversion[dfhdl.options.OnError, OnError] = x => x.asInstanceOf[OnError] + object OnError: + given (using onError: LinterOptions.OnError): OnError = onError + export dfhdl.options.OnError.* + + opaque type WarnAsError <: LinterOptions.WarnAsError = LinterOptions.WarnAsError + given Conversion[LinterOptions.WarnAsError, WarnAsError] = identity + object WarnAsError: + given (using warnAsError: LinterOptions.WarnAsError): WarnAsError = warnAsError +end VerilatorOptions diff --git a/lib/src/main/scala/dfhdl/tools/AvailableTools.scala b/lib/src/main/scala/dfhdl/tools/AvailableTools.scala index 020212d16..e2a92a855 100644 --- a/lib/src/main/scala/dfhdl/tools/AvailableTools.scala +++ b/lib/src/main/scala/dfhdl/tools/AvailableTools.scala @@ -2,8 +2,8 @@ package dfhdl.tools import toolsCore.* object linters: - val verilator: VerilogLinter = Verilator - val ghdl: VHDLLinter = GHDL + given verilator: Verilator.type = Verilator + given ghdl: GHDL.type = GHDL object builders: val vivado: Builder = Vivado diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/GHDL.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/GHDL.scala index 3ab51ce9c..1fdeed550 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/GHDL.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/GHDL.scala @@ -11,8 +11,11 @@ import dfhdl.compiler.analysis.* import java.nio.file.Paths import java.io.FileWriter import java.io.File.separatorChar +import dfhdl.options.GHDLOptions object GHDL extends VHDLLinter: + type LO = GHDLOptions + val toolName: String = "GHDL" def binExec: String = "ghdl" def filesCmdPart[D <: Design](cd: CompiledDesign[D]): String = @@ -34,7 +37,9 @@ object GHDL extends VHDLLinter: // config files must be placed before the design sources s"$globalPackage $designsInCmd" end filesCmdPart - def lint[D <: Design](cd: CompiledDesign[D])(using co: CompilerOptions): CompiledDesign[D] = + def lint[D <: Design]( + cd: CompiledDesign[D] + )(using co: CompilerOptions, lo: LO): CompiledDesign[D] = val std = co.backend match case be: backends.vhdl => be.dialect match @@ -47,7 +52,7 @@ object GHDL extends VHDLLinter: ) exec( cd, - s"$binExec -a --std=$std ${filesCmdPart(cd)}" + s"$binExec -a${lo.warnAsError.toFlag("--warn-error")} --std=$std ${filesCmdPart(cd)}" ) end lint end GHDL diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala index 3a46718c3..f1884d71d 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala @@ -3,8 +3,14 @@ import dfhdl.core.Design import dfhdl.compiler.stages.CompiledDesign import dfhdl.compiler.ir.SourceFile import dfhdl.options.CompilerOptions +import dfhdl.options.ToolOptions +import dfhdl.options.LinterOptions +import dfhdl.options.BuilderOptions +import dfhdl.options.OnError +import java.io.IOException trait Tool: + val toolName: String protected[dfhdl] def preprocess[D <: Design](cd: CompiledDesign[D])(using CompilerOptions ): CompiledDesign[D] = cd @@ -16,29 +22,42 @@ trait Tool: cd.newStage(stagedDB.copy(srcFiles = stagedDB.srcFiles ++ sourceFiles)).commit final protected def exec[D <: Design](cd: CompiledDesign[D], cmd: String)(using - co: CompilerOptions + co: CompilerOptions, + to: ToolOptions ): CompiledDesign[D] = import scala.sys.process.* val pwd = new java.io.File(co.topCommitPath(cd.stagedDB)) val errCode = Process(cmd, pwd).! if (errCode != 0) - sys.exit(errCode) + val msg = s"${toolName} exited with the error code ${errCode}." + to.onError match + case OnError.Exit => + println("msg") + sys.exit(errCode) + case OnError.Exception => sys.error(msg) cd + end exec end Tool trait Linter extends Tool: - def lint[D <: Design](cd: CompiledDesign[D])(using CompilerOptions): CompiledDesign[D] -trait VerilogLinter extends Linter + type LO <: LinterOptions + def lint[D <: Design](cd: CompiledDesign[D])(using CompilerOptions, LO): CompiledDesign[D] +trait VerilogLinterOptions extends LinterOptions +trait VerilogLinter extends Linter: + type LO <: VerilogLinterOptions object VerilogLinter: // default verilog linter will be verilator - given VerilogLinter = dfhdl.tools.linters.verilator -trait VHDLLinter extends Linter + export dfhdl.tools.linters.verilator +trait VHDLLinterOptions extends LinterOptions +trait VHDLLinter extends Linter: + type LO <: VHDLLinterOptions object VHDLLinter: // default vhdl linter will be ghdl - given VHDLLinter = dfhdl.tools.linters.ghdl + export dfhdl.tools.linters.ghdl trait Builder extends Tool: - def build[D <: Design](cd: CompiledDesign[D])(using CompilerOptions): CompiledDesign[D] + type BO <: BuilderOptions + def build[D <: Design](cd: CompiledDesign[D])(using CompilerOptions, BO): CompiledDesign[D] object Builder: // default linter will be vivado given Builder = dfhdl.tools.builders.vivado diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala index 8eb3998d3..56b722006 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala @@ -9,13 +9,16 @@ import dfhdl.compiler.analysis.* import java.nio.file.Paths import java.io.FileWriter import java.io.File.separatorChar +import dfhdl.options.VerilatorOptions object Verilator extends VerilogLinter: + type LO = VerilatorOptions + val toolName: String = "Verilator" def binExec: String = val osName: String = sys.props("os.name").toLowerCase if (osName.contains("windows")) "verilator_bin" else "verilator" - def commonFlags: String = "-Wall" + def commonFlags(using lo: LO): String = s"-Wall${lo.warnAsError.toFlag("--Werror")} " def filesCmdPart[D <: Design](cd: CompiledDesign[D]): String = // We use `forceWindowsToLinuxPath` fit the verilator needs val designsInCmd = cd.stagedDB.srcFiles.view.collect { @@ -45,7 +48,7 @@ object Verilator extends VerilogLinter: CompilerOptions ): CompiledDesign[D] = addSourceFiles(cd, List(new VerilatorConfigPrinter(using cd.stagedDB.getSet).getSourceFile)) - def lint[D <: Design](cd: CompiledDesign[D])(using CompilerOptions): CompiledDesign[D] = + def lint[D <: Design](cd: CompiledDesign[D])(using CompilerOptions, LO): CompiledDesign[D] = exec( cd, s"$binExec --lint-only $commonFlags ${filesCmdPart(cd)}" diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/Vivado.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/Vivado.scala index dc05e134a..f731a898c 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/Vivado.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/Vivado.scala @@ -8,8 +8,12 @@ import dfhdl.options.{PrinterOptions, CompilerOptions} import dfhdl.compiler.printing.Printer import dfhdl.compiler.analysis.* import java.nio.file.Paths +import dfhdl.options.BuilderOptions +trait VivadoOptions extends BuilderOptions object Vivado extends Builder: + type BO = VivadoOptions + val toolName: String = "Vivado" def binExec: String = "vivado" def filesCmdPart[D <: Design](cd: CompiledDesign[D]): String = ??? override protected[dfhdl] def preprocess[D <: Design](cd: CompiledDesign[D])(using @@ -19,7 +23,7 @@ object Vivado extends Builder: cd, List(new VivadoProjectTclConfigPrinter(using cd.stagedDB.getSet).getSourceFile) ) - def build[D <: Design](cd: CompiledDesign[D])(using CompilerOptions): CompiledDesign[D] = + def build[D <: Design](cd: CompiledDesign[D])(using CompilerOptions, BO): CompiledDesign[D] = exec( cd, s"$binExec -mode batch -source ${cd.stagedDB.top.dclName}.tcl" diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/helpers.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/helpers.scala new file mode 100644 index 000000000..cbb452657 --- /dev/null +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/helpers.scala @@ -0,0 +1,6 @@ +package dfhdl.tools.toolsCore + +extension (flagVal: Boolean) + def toFlag(flagName: String): String = + if (flagVal) s" $flagName" + else "" From aa6e7ba9855cafc374528c643712578883efd68a Mon Sep 17 00:00:00 2001 From: oronpo Date: Thu, 16 May 2024 19:42:09 +0300 Subject: [PATCH 4/8] fix #142 change indexes/shifts to DFInt32 type --- .../scala/dfhdl/compiler/ir/DFMember.scala | 2 +- .../main/scala/dfhdl/compiler/ir/DataOps.scala | 2 +- .../dfhdl/compiler/printing/DFValPrinter.scala | 10 ++++------ .../stages/verilog/VerilogValPrinter.scala | 15 ++++++--------- .../compiler/stages/vhdl/VHDLPrinter.scala | 18 +++++++++--------- .../compiler/stages/vhdl/VHDLValPrinter.scala | 10 +++++----- core/src/main/scala/dfhdl/core/DFDecimal.scala | 10 +++------- core/src/main/scala/dfhdl/core/DFVal.scala | 11 +++++++++-- core/src/main/scala/dfhdl/core/DFVector.scala | 2 +- .../src/test/scala/CoreSpec/DFVectorSpec.scala | 2 +- lib/src/test/scala/issues/IssueSpec.scala | 3 +++ lib/src/test/scala/issues/i142.scala | 14 ++++++++++++++ 12 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 lib/src/test/scala/issues/i142.scala diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFMember.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFMember.scala index e62cb01fd..3a7202d93 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFMember.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFMember.scala @@ -521,7 +521,7 @@ object DFVal: object Const: def unapply(applyIdx: ApplyIdx)(using MemberGetSet): Option[Int] = applyIdx.relIdx.get match - case DFVal.Const(DFUInt(_), data: Option[BigInt] @unchecked, _, _, _) => + case DFVal.Const(DFInt32, data: Option[BigInt] @unchecked, _, _, _) => data.map(_.toInt) case _ => None diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DataOps.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DataOps.scala index e493aa787..1d2d72ec3 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DataOps.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DataOps.scala @@ -83,7 +83,7 @@ def calcFuncData[OT <: DFType]( // bits shifting case ( op @ (FuncOp.<< | FuncOp.>>), - DFBits(_) :: DFUInt(_) :: Nil, + DFBits(_) :: DFInt32 :: Nil, (vec: (BitVector, BitVector) @unchecked) :: Some(shift: BigInt) :: Nil ) => op match diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/printing/DFValPrinter.scala b/compiler/ir/src/main/scala/dfhdl/compiler/printing/DFValPrinter.scala index 262ac46d8..7f5d1b8c2 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/printing/DFValPrinter.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/printing/DFValPrinter.scala @@ -9,7 +9,6 @@ extension (ref: DFRef.TwoWayAny) def refCodeString(using printer: AbstractValPrinter): String = printer.csRef(ref, false) def refCodeString(typeCS: Boolean)(using printer: AbstractValPrinter): String = printer.csRef(ref, typeCS) - def simpleRefCodeString(using printer: AbstractValPrinter): String = printer.csSimpleRef(ref) extension (intParamRef: IntParamRef) def refCodeString(typeCS: Boolean)(using printer: AbstractValPrinter): String = intParamRef match @@ -152,10 +151,7 @@ protected trait DFValPrinter extends AbstractValPrinter: case Func.Op.+ | Func.Op.- | Func.Op.`*` if dfVal.dfType.width > argL.get.dfType.width => s"${dfVal.op}^" case op => op.toString - val rhsStr = dfVal.op match - case Func.Op.>> | Func.Op.<< => argR.simpleRefCodeString - case _ => csArgR - s"${csArgL.applyBrackets()} $opStr ${rhsStr.applyBrackets()}" + s"${csArgL.applyBrackets()} $opStr ${csArgR.applyBrackets()}" // unary/postfix func case arg :: Nil => val csArg = arg.refCodeString(typeCS) @@ -240,6 +236,8 @@ protected trait DFValPrinter extends AbstractValPrinter: s"""d"${printer.csWidthInterp(tWidthParamRef)}'$${${relValStr}}"""" case (DFSInt(tWidthParamRef), DFInt32) => s"""sd"${printer.csWidthInterp(tWidthParamRef)}'$${${relValStr}}"""" + case (DFInt32, DFUInt(_) | DFSInt(_)) => + s"${relValStr}.toInt" case _ => throw new IllegalArgumentException("Unsupported alias/conversion") end match @@ -247,7 +245,7 @@ protected trait DFValPrinter extends AbstractValPrinter: def csDFValAliasApplyRange(dfVal: Alias.ApplyRange): String = s"${dfVal.relValCodeString}(${dfVal.relBitHigh}, ${dfVal.relBitLow})" def csDFValAliasApplyIdx(dfVal: Alias.ApplyIdx): String = - val relIdxStr = dfVal.relIdx.simpleRefCodeString + val relIdxStr = dfVal.relIdx.refCodeString s"${dfVal.relValCodeString}($relIdxStr)" // when the tuple field number exceeds this number, the tuple // field selections changes from `dv._${idx+1}` to `dv($idx)` diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogValPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogValPrinter.scala index 0159a12a0..4bfab691d 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogValPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogValPrinter.scala @@ -54,13 +54,10 @@ protected trait VerilogValPrinter extends AbstractValPrinter: case DFSInt(_) => ">>>" case _ => ">>" case op => op.toString - val rhsStr = dfVal.op match - case Func.Op.>> | Func.Op.<< => argR.simpleRefCodeString - case _ => argR.refCodeString if (isInfix) - s"${argL.refCodeString.applyBrackets()} $opStr ${rhsStr.applyBrackets()}" + s"${argL.refCodeString.applyBrackets()} $opStr ${argR.refCodeString.applyBrackets()}" else - s"$opStr(${argL.refCodeString}, ${rhsStr})" + s"$opStr(${argL.refCodeString}, ${argR.refCodeString})" // unary/postfix func case arg :: Nil => val argStr = arg.refCodeString @@ -134,9 +131,9 @@ protected trait VerilogValPrinter extends AbstractValPrinter: s"${tWidthParamRef.refCodeString.applyBrackets()}'($relValStr)" case (DFSInt(tWidthParamRef), DFSInt(_) | DFInt32) => s"${tWidthParamRef.refCodeString.applyBrackets()}'($relValStr)" - case (DFInt32, DFSInt(_)) => relValStr - case (DFBit, DFBool) => relValStr - case (DFBool, DFBit) => relValStr + case (DFInt32, DFUInt(_) | DFSInt(_)) => relValStr + case (DFBit, DFBool) => relValStr + case (DFBool, DFBit) => relValStr case (toStruct: DFStruct, _: DFBits) => s"${toStruct.getName}'($relValStr)" case _ => printer.unsupported @@ -145,7 +142,7 @@ protected trait VerilogValPrinter extends AbstractValPrinter: def csDFValAliasApplyRange(dfVal: Alias.ApplyRange): String = s"${dfVal.relValCodeString}[${dfVal.relBitHigh}:${dfVal.relBitLow}]" def csDFValAliasApplyIdx(dfVal: Alias.ApplyIdx): String = - val relIdxStr = dfVal.relIdx.simpleRefCodeString + val relIdxStr = dfVal.relIdx.refCodeString s"${dfVal.relValCodeString}[$relIdxStr]" // when the tuple field number exceeds this number, the tuple // field selections changes from `dv._${idx+1}` to `dv($idx)` diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala index bc47c87ee..9a63cb053 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala @@ -75,9 +75,9 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt |function to_bool(A : std_logic) return boolean; |function to_bool(A : std_logic_vector(0 downto 0)) return boolean; |function resize(A : std_logic_vector; new_length : integer) return std_logic_vector; - |function slv_sll(slv : std_logic_vector; num_shifts : unsigned) return std_logic_vector; - |function slv_srl(slv : std_logic_vector; num_shifts : unsigned) return std_logic_vector; - |function signed_sra(A : signed; num_shifts : unsigned) return signed; + |function slv_sll(slv : std_logic_vector; num_shifts : integer) return std_logic_vector; + |function slv_srl(slv : std_logic_vector; num_shifts : integer) return std_logic_vector; + |function signed_sra(A : signed; num_shifts : integer) return signed; |end package ${printer.packageName}; | |package body ${printer.packageName} is @@ -174,17 +174,17 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt | return A; | end if; |end; - |function slv_sll(slv : std_logic_vector; num_shifts : unsigned) return std_logic_vector is + |function slv_sll(slv : std_logic_vector; num_shifts : integer) return std_logic_vector is |begin - | return to_slv(unsigned(slv) sll to_integer(num_shifts)); + | return to_slv(unsigned(slv) sll num_shifts); |end; - |function slv_srl(slv : std_logic_vector; num_shifts : unsigned) return std_logic_vector is + |function slv_srl(slv : std_logic_vector; num_shifts : integer) return std_logic_vector is |begin - | return to_slv(unsigned(slv) srl to_integer(num_shifts)); + | return to_slv(unsigned(slv) srl num_shifts); |end; - |function signed_sra(A : signed; num_shifts : unsigned) return signed is + |function signed_sra(A : signed; num_shifts : integer) return signed is |begin - | return shift_right(A, to_integer(num_shifts)); + | return shift_right(A, num_shifts); |end; |end package body ${printer.packageName}; |""".stripMargin diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala index 0a287c236..f8e67d589 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala @@ -61,10 +61,8 @@ protected trait VHDLValPrinter extends AbstractValPrinter: case Func.Op.- => "csub" case Func.Op.`*` => "cmul" case op => op.toString - val rhsStr = dfVal.op match - case Func.Op.>> | Func.Op.<< => argR.simpleRefCodeString - case _ => argR.refCodeString - if (infix) s"${argL.refCodeString.applyBrackets()} $opStr ${rhsStr.applyBrackets()}" + if (infix) + s"${argL.refCodeString.applyBrackets()} $opStr ${argR.refCodeString.applyBrackets()}" else s"${opStr}(${argL.refCodeString}, ${argR.refCodeString})" // unary/postfix func case arg :: Nil => @@ -149,13 +147,15 @@ protected trait VHDLValPrinter extends AbstractValPrinter: s"to_unsigned($relValStr, ${tWidthParamRef.refCodeString})" case (DFSInt(tWidthParamRef), DFInt32) => s"to_signed($relValStr, ${tWidthParamRef.refCodeString})" + case (DFInt32, DFUInt(_) | DFSInt(_)) => + s"to_integer($relValStr)" case _ => printer.unsupported end match end csDFValAliasAsIs def csDFValAliasApplyRange(dfVal: Alias.ApplyRange): String = s"${dfVal.relValCodeString}(${dfVal.relBitHigh} downto ${dfVal.relBitLow})" def csDFValAliasApplyIdx(dfVal: Alias.ApplyIdx): String = - val relIdxStr = dfVal.relIdx.simpleRefCodeString + val relIdxStr = dfVal.relIdx.refCodeString s"${dfVal.relValCodeString}($relIdxStr)" // when the tuple field number exceeds this number, the tuple // field selections changes from `dv._${idx+1}` to `dv($idx)` diff --git a/core/src/main/scala/dfhdl/core/DFDecimal.scala b/core/src/main/scala/dfhdl/core/DFDecimal.scala index 8f2f35914..b841df432 100644 --- a/core/src/main/scala/dfhdl/core/DFDecimal.scala +++ b/core/src/main/scala/dfhdl/core/DFDecimal.scala @@ -1265,9 +1265,8 @@ object DFUInt: object Val: trait UBArg[UB <: IntP, R]: - type OutW <: IntP type OutP - type Out = DFValTP[DFUInt[OutW], OutP] + type Out = DFValTP[DFInt32, OutP] def apply(ub: IntParam[UB], arg: R)(using DFC): Out trait UBArgLP: transparent inline given errorDMZ[UB <: Int, R](using @@ -1285,13 +1284,11 @@ object DFUInt: unsignedCheck: Unsigned.Check[R < 0], ubCheck: `UB > R`.CheckNUB[UB, R] ): UBArg[UB, R] with - type OutW = IntP.Max[IntP.CLog2[UB], 1] type OutP = CONST def apply(ub: IntParam[UB], arg: R)(using DFC): Out = unsignedCheck(arg < 0) ubCheck(ub, arg) - val width = ub.clog2.max(1) - DFVal.Const(DFUInt(width), Some(BigInt(arg))) + DFVal.Const(DFInt32, Some(BigInt(arg))) end fromInt given fromR[UB <: IntP, R, IC <: DFXInt.Val.Candidate[R]](using ic: IC @@ -1299,7 +1296,6 @@ object DFUInt: unsignedCheck: Unsigned.Check[ic.OutS], widthCheck: `UBW == RW`.CheckNUB[IntP.CLog2[UB], ic.OutW] ): UBArg[UB, R] with - type OutW = IntP.CLog2[UB] type OutP = ic.OutP def apply(ub: IntParam[UB], arg: R)(using DFC): Out = val argVal = ic(arg) @@ -1313,7 +1309,7 @@ object DFUInt: summon[`UB > R`.CheckNUB[UB, Int]](ub, value.toInt) case _ => // no check case _ => // no check - argVal.asValTP[DFUInt[OutW], ic.OutP] + DFVal.Alias.AsIs(DFInt32, argVal) end apply end fromR end UBArg diff --git a/core/src/main/scala/dfhdl/core/DFVal.scala b/core/src/main/scala/dfhdl/core/DFVal.scala index 55088d0f1..c1c9f09ad 100644 --- a/core/src/main/scala/dfhdl/core/DFVal.scala +++ b/core/src/main/scala/dfhdl/core/DFVal.scala @@ -756,10 +756,17 @@ object DFVal extends DFValLP: end forced end ApplyRange object ApplyIdx: - def apply[T <: DFTypeAny, W <: IntP, M <: ModifierAny, IW <: Int]( + def apply[ + T <: DFTypeAny, + W <: IntP, + M <: ModifierAny, + IS <: Boolean, + IW <: Int, + IN <: NativeType + ]( dfType: T, relVal: DFVal[DFTypeAny, M], - relIdx: DFValOf[DFUInt[IW]] + relIdx: DFValOf[DFXInt[IS, IW, IN]] )(using DFC): DFVal[T, M] = val alias: ir.DFVal.Alias.ApplyIdx = ir.DFVal.Alias.ApplyIdx( diff --git a/core/src/main/scala/dfhdl/core/DFVector.scala b/core/src/main/scala/dfhdl/core/DFVector.scala index 040af3831..847205232 100644 --- a/core/src/main/scala/dfhdl/core/DFVector.scala +++ b/core/src/main/scala/dfhdl/core/DFVector.scala @@ -160,7 +160,7 @@ object DFVector: import DFDecimal.StrInterpOps.d val elementType = lhs.dfType.cellType Vector.tabulate(lhs.dfType.cellDims.head)(i => - val idxVal = d"$i".asConstOf[DFUInt[Int]] + val idxVal = DFVal.Const(DFInt32, Some(BigInt(i))) DFVal.Alias.ApplyIdx(elementType, lhs, idxVal)(using dfc.anonymize) ) end extension diff --git a/core/src/test/scala/CoreSpec/DFVectorSpec.scala b/core/src/test/scala/CoreSpec/DFVectorSpec.scala index 0d93f4b75..1ebc0f2c2 100644 --- a/core/src/test/scala/CoreSpec/DFVectorSpec.scala +++ b/core/src/test/scala/CoreSpec/DFVectorSpec.scala @@ -17,7 +17,7 @@ class DFVectorSpec extends DFSpec: |val t3 = v1(3) |val i = UInt(3) <> VAR |val i2 = UInt(4) <> VAR - |val t4 = v1(i) + |val t4 = v1(i.toInt) |val v3 = UInt(8) X 4 X 4 <> VAR |v3 := all(all(d"8'0")) |v3(3)(1) := d"8'25" diff --git a/lib/src/test/scala/issues/IssueSpec.scala b/lib/src/test/scala/issues/IssueSpec.scala index 91aabf249..d5b16f410 100644 --- a/lib/src/test/scala/issues/IssueSpec.scala +++ b/lib/src/test/scala/issues/IssueSpec.scala @@ -71,4 +71,7 @@ class IssuesSpec extends FunSuite: |endmodule |""".stripMargin ) + test("i142 compiles and passes VHDL linting"): + given options.CompilerOptions.Backend = backends.vhdl + i142.IntegerIndexingIssue().compile.lint end IssuesSpec diff --git a/lib/src/test/scala/issues/i142.scala b/lib/src/test/scala/issues/i142.scala new file mode 100644 index 000000000..3d06ca52c --- /dev/null +++ b/lib/src/test/scala/issues/i142.scala @@ -0,0 +1,14 @@ +// format: off +package issues.i142 + +import dfhdl._ + +class IntegerIndexingIssue() extends RTDesign: + val a = Bits(4) <> IN + val b = Bits(12) X 16 <> VAR.REG init all(all(0)) + val c = Bits(12) <> OUT + + c := b(a) + +given options.CompilerOptions.PrintGenFiles = true +given options.CompilerOptions.Backend = backends.vhdl.v2008 \ No newline at end of file From 171a8b6263d5eccc6a7f3b701a6d7190b2ffdb48 Mon Sep 17 00:00:00 2001 From: oronpo Date: Fri, 17 May 2024 06:14:58 +0300 Subject: [PATCH 5/8] prepare for Scala 3.5 plugin changes --- build.sbt | 14 +++++++++++++ .../{scala => scala-3.4-}/plugin/Plugin.scala | 0 .../src/main/scala-3.5+/plugin/Plugin.scala | 20 +++++++++++++++++++ 3 files changed, 34 insertions(+) rename plugin/src/main/{scala => scala-3.4-}/plugin/Plugin.scala (100%) mode change 100755 => 100644 create mode 100644 plugin/src/main/scala-3.5+/plugin/Plugin.scala diff --git a/build.sbt b/build.sbt index b88b1a444..badfe8442 100755 --- a/build.sbt +++ b/build.sbt @@ -50,12 +50,26 @@ lazy val internals = project libraryDependencies ++= commonDependencies ) +def additionalSources(scalaVersion: String, base: File): Seq[File] = { + CrossVersion.partialVersion(scalaVersion) match { + case Some((3, minor)) if minor <= 4 => + Seq(base / "src" / "main" / "scala-3.4-") + case Some((3, minor)) if minor >= 5 => + Seq(base / "src" / "main" / "scala-3.5+") + case _ => + Seq.empty + } +} lazy val plugin = project .settings( name := s"$projectName-plugin", settings, crossTarget := target.value / s"scala-${scalaVersion.value}", // workaround for https://github.com/sbt/sbt/issues/5097 crossVersion := CrossVersion.full, + Compile / unmanagedSourceDirectories ++= { + val base = baseDirectory.value + additionalSources(scalaVersion.value, base) + }, libraryDependencies += "org.scala-lang" %% "scala3-compiler" % compilerVersion % "provided" ).dependsOn(internals) diff --git a/plugin/src/main/scala/plugin/Plugin.scala b/plugin/src/main/scala-3.4-/plugin/Plugin.scala old mode 100755 new mode 100644 similarity index 100% rename from plugin/src/main/scala/plugin/Plugin.scala rename to plugin/src/main/scala-3.4-/plugin/Plugin.scala diff --git a/plugin/src/main/scala-3.5+/plugin/Plugin.scala b/plugin/src/main/scala-3.5+/plugin/Plugin.scala new file mode 100644 index 000000000..fa9f0d353 --- /dev/null +++ b/plugin/src/main/scala-3.5+/plugin/Plugin.scala @@ -0,0 +1,20 @@ +package dfhdl.plugin + +import dotty.tools.dotc.plugins.* +import dotty.tools.dotc.core.Contexts.Context + +class Plugin extends StandardPlugin: + val name: String = "dfhdl.plugin" + override val description: String = "Dedicated DSL capabilities for DFiant HDL" + + override def initialize(options: List[String])(using Context): List[PluginPhase] = + val setting = new Setting(options.headOption) + MetaContextPlacerPhase(setting) :: + CustomControlPhase(setting) :: + DesignDefsPhase(setting) :: + MetaContextDelegatePhase(setting) :: + MetaContextGenPhase(setting) :: + OnCreateEventsPhase(setting) :: + FixInterpDFValPhase(setting) :: + Nil +end Plugin From 25a1a5e451bdb51c9e03540a17373d408203f949 Mon Sep 17 00:00:00 2001 From: oronpo Date: Fri, 17 May 2024 06:15:24 +0300 Subject: [PATCH 6/8] prepare for Scala 3.4.2 changes --- lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala index f1884d71d..8e8163df2 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/Tool.scala @@ -30,9 +30,10 @@ trait Tool: val errCode = Process(cmd, pwd).! if (errCode != 0) val msg = s"${toolName} exited with the error code ${errCode}." - to.onError match + // TODO: there is a false exhaustivity warning here in 3.4.2 or later + (to.onError: @unchecked) match case OnError.Exit => - println("msg") + println(msg) sys.exit(errCode) case OnError.Exception => sys.error(msg) cd From 77fabacbe9bc0e6c3c0fd67ff788244ca2fb6d35 Mon Sep 17 00:00:00 2001 From: oronpo Date: Fri, 17 May 2024 06:16:15 +0300 Subject: [PATCH 7/8] issues will always trigger exception instead of exit --- lib/src/test/scala/issues/IssueSpec.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/test/scala/issues/IssueSpec.scala b/lib/src/test/scala/issues/IssueSpec.scala index d5b16f410..3fac712c5 100644 --- a/lib/src/test/scala/issues/IssueSpec.scala +++ b/lib/src/test/scala/issues/IssueSpec.scala @@ -5,6 +5,8 @@ import dfhdl.* import dfhdl.compiler.stages.getCompiledCodeString class IssuesSpec extends FunSuite: + given options.OnError = options.OnError.Exception + test("i116 compiles with no exception"): i116.GlobCounter(64).compile test("i118 compiles and passes VHDL linting"): From 95ffa5f2625ccdec9e599d8b56c2df085e873d5e Mon Sep 17 00:00:00 2001 From: oronpo Date: Sat, 18 May 2024 10:15:15 +0300 Subject: [PATCH 8/8] Fix #141 proper vector/struct/opaque conversions --- .../main/scala/dfhdl/compiler/ir/DFType.scala | 24 +- .../stages/vhdl/VHDLOwnerPrinter.scala | 66 ++++- .../compiler/stages/vhdl/VHDLPrinter.scala | 86 ++++++- .../stages/vhdl/VHDLTypePrinter.scala | 144 ++++++++--- .../compiler/stages/vhdl/VHDLValPrinter.scala | 30 ++- .../scala/StagesSpec/PrintVHDLCodeSpec.scala | 226 +++++++++++++++--- lib/src/test/scala/issues/IssueSpec.scala | 3 + lib/src/test/scala/issues/i141.scala | 13 + 8 files changed, 500 insertions(+), 92 deletions(-) create mode 100644 lib/src/test/scala/issues/i141.scala diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala index b6ac5bfb0..af79eac9d 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala @@ -29,8 +29,24 @@ object DFType: case dt: T => Some(dt) case _ => None end Companion + + extension (dfType: DFType) + def decompose[B <: DFType](pf: PartialFunction[DFType, B] = a => a)(using + MemberGetSet + ): ListSet[B] = + val deps: Iterable[DFType] = dfType match + case dt: DFStruct => + dt.fieldMap.values.flatMap(_.decompose(pf)) + case dt: DFOpaque => + dt.actualType.decompose(pf) + case dt: DFVector => + dt.cellType.decompose(pf) + case _ => Nil + ListSet.from(deps.collect(pf) ++ List(dfType).collect(pf)) + end DFType +sealed trait ComposedDFType extends DFType sealed trait NamedDFType extends DFType, NamedGlobal object NamedDFTypes: def unapply(dfVal: DFVal)(using MemberGetSet): Option[ListSet[NamedDFType]] = @@ -200,7 +216,7 @@ object DFEnum extends DFType.Companion[DFEnum, Option[BigInt]] final case class DFVector( cellType: DFType, cellDims: List[Int] -) extends DFType: +) extends ComposedDFType: type Data = Vector[Any] def width(using MemberGetSet): Int = cellType.width * cellDims.product def createBubbleData(using MemberGetSet): Data = Vector.fill(cellDims.head)( @@ -234,7 +250,8 @@ object DFVector extends DFType.Companion[DFVector, Vector[Any]] // DFOpaque ///////////////////////////////////////////////////////////////////////////// final case class DFOpaque(protected val name: String, id: DFOpaque.Id, actualType: DFType) - extends NamedDFType: + extends NamedDFType, + ComposedDFType: type Data = Any def width(using MemberGetSet): Int = actualType.width def createBubbleData(using MemberGetSet): Data = actualType.createBubbleData @@ -258,7 +275,8 @@ object DFOpaque extends DFType.Companion[DFOpaque, Any]: final case class DFStruct( protected val name: String, fieldMap: ListMap[String, DFType] -) extends NamedDFType: +) extends NamedDFType, + ComposedDFType: type Data = List[Any] def getNameForced: String = name def width(using MemberGetSet): Int = fieldMap.values.map(_.width).sum diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLOwnerPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLOwnerPrinter.scala index 58a03d270..04f2aff9e 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLOwnerPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLOwnerPrinter.scala @@ -6,7 +6,8 @@ import dfhdl.internals.* import DFVal.* import dfhdl.compiler.ir.ProcessBlock.Sensitivity import dfhdl.compiler.ir.DFConditional.DFCaseBlock.Pattern - +import scala.collection.mutable +import scala.collection.immutable.ListSet protected trait VHDLOwnerPrinter extends AbstractOwnerPrinter: type TPrinter <: VHDLPrinter val useStdSimLibrary: Boolean = true @@ -49,15 +50,60 @@ protected trait VHDLOwnerPrinter extends AbstractOwnerPrinter: end csEntityDcl def archName(design: DFDesignBlock): String = s"${design.dclName}_arch" def csArchitectureDcl(design: DFDesignBlock): String = - val localTypeDcls = printer.csLocalTypeDcls(design).emptyOr(x => s"$x\n") - val vectorTypeDcls = - printer.getLocalVectorTypes(design).view.map(printer.csDFVectorDclsLocal) - .mkString("\n").emptyOr(x => s"$x\n") - val structConvFuncs = - getSet.designDB.getLocalNamedDFTypes(design).view - .collect { case dfType: DFStruct => printer.csDFStructConvFuncsBody(dfType) } - .mkString("\n").emptyOr(x => s"$x\n") val designMembers = design.members(MemberView.Folded) + // collecting all the vhdl named types that are used in conversion to/from bits + val vhdlNamedConvDFTypes = design.members(MemberView.Flattened).view.flatMap { + case alias: DFVal.Alias.AsIs => + val pf: PartialFunction[DFType, (DFVector | NamedDFType)] = { + case dt: (DFVector | NamedDFType) => dt + } + (alias.dfType, alias.relValRef.get.dfType) match + case (DFBits(_), fromDFType: (NamedDFType | ComposedDFType)) => + fromDFType.decompose(pf) + case (toDFType: (NamedDFType | ComposedDFType), DFBits(_)) => + toDFType.decompose(pf) + case _ => None + case _ => None + }.toSet + // the vectors requiring conversion to/from bits + val vectorsConvUsed = vhdlNamedConvDFTypes.collect { case dfType: DFVector => + printer.getVecDepthAndCellTypeName(dfType)._1 + } + // In VHDL the vectors need to be named, and put in dependency order of other named types. + // So first we prepare the vector type declarations in a mutable map and later we remove + // entries that were already placed in the final type printing. + val vectorTypeDcls = + mutable.Map.from(printer.getLocalVectorTypes(design).view.map { (tpName, depth) => + val dclScope = + if (vectorsConvUsed.contains(tpName)) DclScope.ArchBody else DclScope.TypeOnly + tpName -> printer.csDFVectorDclsLocal(dclScope)(tpName, depth) + }) + // collect the local named types, including vectors + val namedDFTypes = ListSet.from(getSet.designDB.designMemberTable(design).view.collect { + case localVar @ DclVar() => localVar.dfType + case localConst @ DclConst() => localConst.dfType + }.flatMap(_.decompose { case dt: (DFVector | NamedDFType) => dt })) + // declarations of the types and relevant functions + val namedTypeConvFuncsDcl = namedDFTypes.view + .flatMap { + // vector types can have different dimensions, but we only need the declaration once + case dfType: DFVector => + val tpName = printer.getVecDepthAndCellTypeName(dfType)._1 + vectorTypeDcls.get(tpName) match + case Some(desc) => + vectorTypeDcls -= tpName + Some(desc) + case None => None + case dfType: NamedDFType => + if (vhdlNamedConvDFTypes.contains(dfType)) + List( + printer.csNamedDFTypeDcl(dfType, global = false), + printer.csNamedDFTypeConvFuncsBody(dfType) + ) + else Some(printer.csNamedDFTypeDcl(dfType, global = false)) + } + .mkString("\n").emptyOr(x => s"$x\n") + val dfValDcls = designMembers.view .flatMap { @@ -70,7 +116,7 @@ protected trait VHDLOwnerPrinter extends AbstractOwnerPrinter: .toList .emptyOr(_.mkString("\n")) val declarations = - s"$localTypeDcls$vectorTypeDcls$structConvFuncs$dfValDcls".emptyOr(v => s"\n${v.hindent}") + s"$namedTypeConvFuncsDcl$dfValDcls".emptyOr(v => s"\n${v.hindent}") val statements = csDFMembers(designMembers.filter { case _: DFVal.Dcl => false case DclConst() => false diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala index 9a63cb053..a7cb82a3f 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLPrinter.scala @@ -4,7 +4,8 @@ import dfhdl.compiler.ir.* import dfhdl.compiler.analysis.* import dfhdl.internals.* import dfhdl.options.PrinterOptions - +import scala.collection.mutable +import scala.collection.immutable.ListSet class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOptions) extends Printer, VHDLTypePrinter, @@ -44,23 +45,51 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt def globalFileName: String = s"${printer.packageName}.vhd" def designFileName(designName: String): String = s"$designName.vhd" override def csGlobalFileContent: String = - val vectorTypeDcls = - printer.globalVectorTypes.view.map(printer.csDFVectorDclsGlobal) + // In VHDL the vectors need to be named, and put in dependency order of other named types. + // So first we prepare the vector type declarations in a mutable map and later we remove + // entries that were already placed in the final type printing. + val vectorTypeDcls = mutable.Map.from( + printer.globalVectorTypes.view.map((tpName, depth) => + tpName -> printer.csDFVectorDclsGlobal(DclScope.Pkg)(tpName, depth) + ) + ) + // The body declarations can be in any order, as long as it's consistent between compilations. + val vectorTypeDclsBody = + printer.globalVectorTypes.view.map(printer.csDFVectorDclsGlobal(DclScope.PkgBody)) .mkString("\n").emptyOr(x => s"$x\n") - val structConvFuncsDcl = + // collect the global named types, including vectors + val namedDFTypes = ListSet.from(getSet.designDB.members.view.collect { + case port @ DclPort() => port.dfType + case const @ DclConst() if const.isGlobal => const.dfType + }.flatMap(_.decompose { case dt: (DFVector | NamedDFType) => dt })) + // declarations of the types and relevant functions + val namedTypeConvFuncsDcl = namedDFTypes.view + .flatMap { + // vector types can have different dimensions, but we only need the declaration once + case dfType: DFVector => + val tpName = printer.getVecDepthAndCellTypeName(dfType)._1 + vectorTypeDcls.get(tpName) match + case Some(desc) => + vectorTypeDcls -= tpName + Some(desc) + case None => None + case dfType: NamedDFType => + List( + printer.csNamedDFTypeDcl(dfType, global = true), + printer.csNamedDFTypeConvFuncsDcl(dfType) + ) + } + .mkString("\n").emptyOr(x => s"$x\n") + val namedTypeConvFuncsBody = getSet.designDB.getGlobalNamedDFTypes.view - .collect { case dfType: DFStruct => printer.csDFStructConvFuncsDcl(dfType) } - .mkString("\n").emptyOr(x => s"$x\n") - val structConvFuncsBody = - getSet.designDB.getGlobalNamedDFTypes.view - .collect { case dfType: DFStruct => printer.csDFStructConvFuncsBody(dfType) } + .collect { case dfType: NamedDFType => printer.csNamedDFTypeConvFuncsBody(dfType) } .mkString("\n").emptyOr(x => s"$x\n") s"""library ieee; |use ieee.std_logic_1164.all; |use ieee.numeric_std.all; | |package ${printer.packageName} is - |${super.csGlobalFileContent + vectorTypeDcls + structConvFuncsDcl} + |${csGlobalConstDcls + namedTypeConvFuncsDcl} |function cadd(A, B : unsigned) return unsigned; |function cadd(A, B : signed) return signed; |function csub(A, B : unsigned) return unsigned; @@ -68,12 +97,19 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt |function clog2(n : natural) return natural; |function to_slv(A : unsigned) return std_logic_vector; |function to_slv(A : signed) return std_logic_vector; + |function to_slv(A : integer) return std_logic_vector; |function to_slv(A : boolean) return std_logic_vector; |function to_slv(A : std_logic) return std_logic_vector; |function to_sl(A : boolean) return std_logic; |function to_sl(A : std_logic_vector(0 downto 0)) return std_logic; |function to_bool(A : std_logic) return boolean; |function to_bool(A : std_logic_vector(0 downto 0)) return boolean; + |function bitWidth(A : std_logic_vector) return integer; + |function bitWidth(A : unsigned) return integer; + |function bitWidth(A : signed) return integer; + |function bitWidth(A : integer) return integer; + |function bitWidth(A : boolean) return integer; + |function bitWidth(A : std_logic) return integer; |function resize(A : std_logic_vector; new_length : integer) return std_logic_vector; |function slv_sll(slv : std_logic_vector; num_shifts : integer) return std_logic_vector; |function slv_srl(slv : std_logic_vector; num_shifts : integer) return std_logic_vector; @@ -81,7 +117,7 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt |end package ${printer.packageName}; | |package body ${printer.packageName} is - |$structConvFuncsBody + |${namedTypeConvFuncsBody + vectorTypeDclsBody} |function cadd(A, B : unsigned) return unsigned is |begin | return unsigned('0' & A) + unsigned('0' & B); @@ -116,6 +152,10 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt |begin | return std_logic_vector(A); |end; + |function to_slv(A : integer) return std_logic_vector is + |begin + | return std_logic_vector(to_signed(A, 32)); + |end; |function to_slv(A : boolean) return std_logic_vector is |begin | if A then @@ -164,6 +204,30 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt | return false; | end if; |end; + |function bitWidth(A : std_logic_vector) return integer is + |begin + | return A'length; + |end; + |function bitWidth(A : unsigned) return integer is + |begin + | return A'length; + |end; + |function bitWidth(A : signed) return integer is + |begin + | return A'length; + |end; + |function bitWidth(A : integer) return integer is + |begin + | return 32; + |end; + |function bitWidth(A : boolean) return integer is + |begin + | return 1; + |end; + |function bitWidth(A : std_logic) return integer is + |begin + | return 1; + |end; |function resize(A : std_logic_vector; new_length : integer) return std_logic_vector is |begin | if new_length > A'length then diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLTypePrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLTypePrinter.scala index d065b2748..be474b289 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLTypePrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLTypePrinter.scala @@ -7,6 +7,9 @@ import dfhdl.internals.* import scala.collection.mutable import scala.collection.immutable.{ListSet, ListMap} import scala.annotation.tailrec + +enum DclScope derives CanEqual: + case TypeOnly, Pkg, PkgBody, ArchBody protected trait VHDLTypePrinter extends AbstractTypePrinter: type TPrinter <: VHDLPrinter def csDFBoolOrBit(dfType: DFBoolOrBit, typeCS: Boolean): String = @@ -29,6 +32,24 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter: case (false, _) => ??? case (true, _) => ??? + def csNamedDFTypeConvFuncsDcl(dfType: NamedDFType): String = + val typeName = dfType match + case dt: DFEnum => csDFEnumTypeName(dt) + case dt: DFStruct => csDFStructTypeName(dt) + case dt: DFOpaque => csDFOpaqueTypeName(dt) + val bitWidth = s"function bitWidth(A: ${typeName}) return integer;" + val to_slv = s"function to_slv(A: ${typeName}) return std_logic_vector;" + val to_typeName = s"function to_${typeName}(A: std_logic_vector) return ${typeName};" + dfType match + // since working on a VHDL subtype, opaques require only `to_typeName` function, + // and `bitWidth` and `to_slv` of the actual type are applicable + case dt: DFOpaque => to_typeName + case _ => s"$bitWidth\n$to_slv\n$to_typeName" + def csNamedDFTypeConvFuncsBody(dfType: NamedDFType): String = + dfType match + case dt: DFEnum => "" + case dt: DFStruct => csDFStructConvFuncsBody(dt) + case dt: DFOpaque => csDFOpaqueConvFuncsBody(dt) def csDFEnumTypeName(dfType: DFEnum): String = s"t_enum_${dfType.getName}" def csDFEnumDcl(dfType: DFEnum, global: Boolean): String = val enumName = dfType.getName @@ -40,17 +61,8 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter: s"type ${csDFEnumTypeName(dfType)} is (\n$entries\n);" def csDFEnum(dfType: DFEnum, typeCS: Boolean): String = csDFEnumTypeName(dfType) def getVectorTypes(dcls: Iterable[DFVal]): ListMap[String, Int] = - def flatten(dfType: DFType): List[DFVector] = - dfType match - case dt: DFStruct => - dt.fieldMap.values.flatMap(flatten).toList - case dt: DFOpaque => - flatten(dt.actualType) - case dt: DFVector => - dt :: flatten(dt.cellType) - case _ => Nil ListMap.from( - dcls.flatMap(dcl => flatten(dcl.dfType).reverse) + dcls.view.flatMap(dcl => dcl.dfType.decompose { case dt: DFVector => dt }) .map(getVecDepthAndCellTypeName) .foldLeft(ListMap.empty[String, Int]) { case (listMap, (cellTypeName, depth)) => listMap.updatedWith(cellTypeName)(prevDepthOpt => @@ -85,16 +97,87 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter: def csDFVectorDclName(dfType: DFVector): String = val (cellTypeName, depth) = getVecDepthAndCellTypeName(dfType) csDFVectorDclName(cellTypeName, depth) - def csDFVectorDcl(cellTypeName: String, depth: Int): String = + def csDFVectorDcl(dclScope: DclScope)(cellTypeName: String, depth: Int): String = val ofTypeName = if (depth == 1) cellTypeName else csDFVectorDclName(cellTypeName, depth - 1) - s"type ${csDFVectorDclName(cellTypeName, depth)} is array (natural range <>) of $ofTypeName;" - def csDFVectorDcls(cellTypeName: String, depth: Int, start: Int): String = - (for (i <- start to depth) yield csDFVectorDcl(cellTypeName, i)) + val typeName = csDFVectorDclName(cellTypeName, depth) + val typeDcl = + s"type $typeName is array (natural range <>) of $ofTypeName;" + val dimArgs = (depth to 1 by -1).map(i => s"D$i : integer").mkString("; ", "; ", "") + val cellDimArg = cellTypeName match + case "std_logic_vector" | "unsigned" | "signed" => "; D0 : integer" + case _ => "" + val funcDcl = + s"""|function bitWidth(A: ${typeName}) return integer; + |function to_slv(A: ${typeName}) return std_logic_vector; + |function to_${typeName}(A: std_logic_vector$dimArgs$cellDimArg) return ${typeName};""".stripMargin + val toSLV = ofTypeName match + case "std_logic_vector" => "A(i)" + case _ => s"to_slv(A(i))" + val dims = (depth to 1 by -1).map(i => s"(0 to D$i - 1)").mkString + val cellDim = cellDimArg.emptyOr(_ => "(D0 - 1 downto 0)") + val argSel = "A(hi downto lo)" + val toCellConv = + if (depth == 1) + cellTypeName match + case "std_logic_vector" => argSel + case "std_logic" => s"to_sl($argSel)" + case "boolean" => s"to_bool($argSel)" + case "unsigned" | "signed" => s"$cellTypeName($argSel)" + case _ => s"to_${cellTypeName}($argSel)" + else + val dimArgsApply = (depth - 1 to 1 by -1).map(i => s"D$i").mkString(", ", ", ", "") + val cellDimArgApply = cellDimArg.emptyOr(_ => ", D0") + s"to_${csDFVectorDclName(cellTypeName, depth - 1)}($argSel$dimArgsApply$cellDimArgApply)" + val cellBitWidth = + val dims = (depth - 1 to 1 by -1).map(i => s"D$i") + val cellDim = if (cellDimArg.isEmpty) s"bitWidth($ofTypeName)" else "D0" + (dims :+ cellDim).mkString(" * ") + val funcBody = + s"""|function bitWidth(A : ${typeName}) return integer is + |begin + | return A'length * bitWidth(A(0)); + |end; + |function to_slv(A : ${typeName}) return std_logic_vector is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer; + | variable ret : std_logic_vector(bitWidth(A) - 1 downto 0); + |begin + | cellBitWidth := bitWidth(A(0)); + | lo := bitWidth(A); + | for i in 0 to A'length-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(hi downto lo) := $toSLV; + | end loop; + | return ret; + |end; + |function to_${typeName}(A : std_logic_vector$dimArgs$cellDimArg) return ${typeName} is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer := $cellBitWidth; + | variable ret : ${typeName}$dims$cellDim; + |begin + | lo := A'length; + | for i in 0 to D${depth}-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(i) := $toCellConv; + | end loop; + | return ret; + |end;""".stripMargin + dclScope match + case DclScope.TypeOnly => typeDcl + case DclScope.Pkg => s"$typeDcl\n$funcDcl" + case DclScope.PkgBody => funcBody + case DclScope.ArchBody => s"$typeDcl\n$funcBody" + end csDFVectorDcl + + def csDFVectorDcls(dclScope: DclScope)(cellTypeName: String, depth: Int, start: Int): String = + (for (i <- start to depth) yield csDFVectorDcl(dclScope)(cellTypeName, i)) .mkString("\n") - def csDFVectorDclsGlobal(cellTypeName: String, depth: Int): String = - csDFVectorDcls(cellTypeName, depth, 1) - def csDFVectorDclsLocal(cellTypeName: String, depth: Int): String = - csDFVectorDcls(cellTypeName, depth, globalVectorTypes.getOrElse(cellTypeName, 0) + 1) + def csDFVectorDclsGlobal(dclScope: DclScope)(cellTypeName: String, depth: Int): String = + csDFVectorDcls(dclScope)(cellTypeName, depth, 1) + def csDFVectorDclsLocal(dclScope: DclScope)(cellTypeName: String, depth: Int): String = + csDFVectorDcls(dclScope)(cellTypeName, depth, globalVectorTypes.getOrElse(cellTypeName, 0) + 1) def csDFVector(dfType: DFVector, typeCS: Boolean): String = if (typeCS) csDFVectorDclName(dfType) else @@ -120,6 +203,12 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter: def csDFOpaqueDcl(dfType: DFOpaque): String = s"subtype ${csDFOpaqueTypeName(dfType)} is ${csDFType(dfType.actualType)};" def csDFOpaque(dfType: DFOpaque, typeCS: Boolean): String = csDFOpaqueTypeName(dfType) + def csDFOpaqueConvFuncsBody(dfType: DFOpaque): String = + val typeName = csDFOpaqueTypeName(dfType) + s"""|function to_${typeName}(A : std_logic_vector) return ${typeName} is + |begin + | return ${printer.csBitsToType(dfType.actualType, "A")}; + |end;""".stripMargin def csDFStructTypeName(dfType: DFStruct): String = s"t_struct_${dfType.getName}" def csDFStructDcl(dfType: DFStruct): String = val fields = dfType.fieldMap.view @@ -128,32 +217,19 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter: .hindent(1) s"type ${csDFStructTypeName(dfType)} is record\n$fields\nend record;" def csDFStruct(dfType: DFStruct, typeCS: Boolean): String = csDFStructTypeName(dfType) - def csDFStructConvFuncsDcl(dfType: DFStruct): String = - val typeName = csDFStructTypeName(dfType) - s"""|function bitWidth(A: ${typeName}) return integer; - |function to_slv(A: ${typeName}) return std_logic_vector; - |function to_${typeName}(A: std_logic_vector) return ${typeName};""".stripMargin def csDFStructConvFuncsBody(dfType: DFStruct): String = val typeName = csDFStructTypeName(dfType) - def bitWidthFunc(dfType: DFType, csArg: String): String = dfType match - case DFBits(_) | DFUInt(_) | DFSInt(_) => s"$csArg'length" - case _: DFBoolOrBit => "1" - case DFVector(cellType, cellDims) => - s"${cellDims.head} * ${bitWidthFunc(cellType, s"$csArg(0)")}" - case dfType: DFStruct => s"bitWidth($csArg)" - case dfType: DFOpaque => bitWidthFunc(dfType.actualType, csArg) - case _ => printer.unsupported def to_slv(fromType: DFType, csArg: String): String = fromType match case DFBits(_) => csArg case _ => s"to_slv($csArg)" val fieldLengths = dfType.fieldMap.map { (n, t) => - s"width := width + ${bitWidthFunc(t, s"A.$n")};" + s"width := width + bitWidth(A.$n);" }.mkString("\n ") val vecAssignments = dfType.fieldMap.map { (n, t) => - s"hi := lo - 1; lo := hi - ${bitWidthFunc(t, s"A.$n")} + 1; ret(hi downto lo) := ${to_slv(t, s"A.$n")};" + s"hi := lo - 1; lo := hi - bitWidth(A.$n) + 1; ret(hi downto lo) := ${to_slv(t, s"A.$n")};" }.mkString("\n ") val fieldAssignments = dfType.fieldMap.map { (n, t) => - s"hi := lo - 1; lo := hi - ${bitWidthFunc(t, s"ret.$n")} + 1; ret.$n := ${printer.csBitsToType(t, "A(hi downto lo)")};" + s"hi := lo - 1; lo := hi - bitWidth(ret.$n) + 1; ret.$n := ${printer.csBitsToType(t, "A(hi downto lo)")};" }.mkString("\n ") s"""|function bitWidth(A : ${typeName}) return integer is | variable width : integer; diff --git a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala index f8e67d589..8f75fa43d 100644 --- a/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala +++ b/compiler/stages/src/main/scala/dfhdl/compiler/stages/vhdl/VHDLValPrinter.scala @@ -104,12 +104,30 @@ protected trait VHDLValPrinter extends AbstractValPrinter: .map(_.refCodeString.applyBrackets()) .mkString(s" ${dfVal.op} ") def csBitsToType(toType: DFType, csArg: String): String = toType match - case DFBits(_) => csArg - case DFBool => s"to_bool($csArg)" - case DFBit => s"to_sl($csArg)" - case DFUInt(_) => s"unsigned($csArg)" - case DFSInt(_) => s"signed($csArg)" - case dfType: DFStruct => s"to_${dfType.getName}($csArg)" + case DFBits(_) => csArg + case DFBool => s"to_bool($csArg)" + case DFBit => s"to_sl($csArg)" + case DFUInt(_) => s"unsigned($csArg)" + case DFSInt(_) => s"signed($csArg)" + case dfType: DFVector => + var loopType: DFType = dfType + var desc: String = s"to_${printer.csDFVectorDclName(dfType)}($csArg" + var inVector: Boolean = true + while (inVector) + loopType match + case dfType: DFVector => + desc = s"$desc, ${dfType.cellDims.head}" + loopType = dfType.cellType + case cellType => + val finale = cellType match + case DFBits(width) => s", ${width.refCodeString}" + case DFUInt(width) => s", ${width.refCodeString}" + case DFSInt(width) => s", ${width.refCodeString}" + case _ => "" + desc = desc + finale + inVector = false + s"$desc)" + case dfType: DFStruct => s"to_${printer.csDFStructTypeName(dfType)}($csArg)" case dfType: DFOpaque => csBitsToType(dfType.actualType, csArg) case _ => printer.unsupported diff --git a/compiler/stages/src/test/scala/StagesSpec/PrintVHDLCodeSpec.scala b/compiler/stages/src/test/scala/StagesSpec/PrintVHDLCodeSpec.scala index 5d1ef9621..462da658c 100644 --- a/compiler/stages/src/test/scala/StagesSpec/PrintVHDLCodeSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/PrintVHDLCodeSpec.scala @@ -293,34 +293,6 @@ class PrintVHDLCodeSpec extends StageSpec: | end record; | type t_vecX1_std_logic_vector is array (natural range <>) of std_logic_vector; | type t_vecX2_std_logic_vector is array (natural range <>) of t_vecX1_std_logic_vector; - | function bitWidth(A : t_struct_DFTuple2) return integer is - | variable width : integer; - | begin - | width := 0; - | width := width + A._1'length; - | width := width + 1; - | return width; - | end; - | function to_slv(A : t_struct_DFTuple2) return std_logic_vector is - | variable hi : integer; - | variable lo : integer; - | variable ret : std_logic_vector(bitWidth(A) - 1 downto 0); - | begin - | lo := bitWidth(A); - | hi := lo - 1; lo := hi - A._1'length + 1; ret(hi downto lo) := A._1; - | hi := lo - 1; lo := hi - 1 + 1; ret(hi downto lo) := to_slv(A._2); - | return ret; - | end; - | function to_t_struct_DFTuple2(A : std_logic_vector) return t_struct_DFTuple2 is - | variable hi : integer; - | variable lo : integer; - | variable ret : t_struct_DFTuple2; - | begin - | lo := A'length; - | hi := lo - 1; lo := hi - ret._1'length + 1; ret._1 := A(hi downto lo); - | hi := lo - 1; lo := hi - 1 + 1; ret._2 := to_sl(A(hi downto lo)); - | return ret; - | end; | constant c01 : std_logic := '0'; | constant c02 : std_logic := '1'; | constant c03 : std_logic := '-'; @@ -415,4 +387,202 @@ class PrintVHDLCodeSpec extends StageSpec: |end Blinker_arch;""".stripMargin ) } + test("Opaque and vector local example") { + case class Foo() extends Opaque(Bits(12) X 16 X 10) + class Example() extends RTDesign: + val x = Bits(1920) <> IN + val v = Foo <> VAR.REG init all(all(all(0))).as(Foo) + val y = Bits(1920) <> OUT + v.din := x.as(Foo) + y := v.bits + + val top = (Example()).getCompiledCodeString + assertNoDiff( + top, + """|library ieee; + |use ieee.std_logic_1164.all; + |use ieee.numeric_std.all; + |use work.Example_pkg.all; + | + |entity Example is + |port ( + | clk : in std_logic; + | rst : in std_logic; + | x : in std_logic_vector(1919 downto 0); + | y : out std_logic_vector(1919 downto 0) + |); + |end Example; + | + |architecture Example_arch of Example is + | type t_vecX1_std_logic_vector is array (natural range <>) of std_logic_vector; + | function bitWidth(A : t_vecX1_std_logic_vector) return integer is + | begin + | return A'length * bitWidth(A(0)); + | end; + | function to_slv(A : t_vecX1_std_logic_vector) return std_logic_vector is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer; + | variable ret : std_logic_vector(bitWidth(A) - 1 downto 0); + | begin + | cellBitWidth := bitWidth(A(0)); + | lo := bitWidth(A); + | for i in 0 to A'length-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(hi downto lo) := A(i); + | end loop; + | return ret; + | end; + | function to_t_vecX1_std_logic_vector(A : std_logic_vector; D1 : integer; D0 : integer) return t_vecX1_std_logic_vector is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer := D0; + | variable ret : t_vecX1_std_logic_vector(0 to D1 - 1)(D0 - 1 downto 0); + | begin + | lo := A'length; + | for i in 0 to D1-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(i) := A(hi downto lo); + | end loop; + | return ret; + | end; + | type t_vecX2_std_logic_vector is array (natural range <>) of t_vecX1_std_logic_vector; + | function bitWidth(A : t_vecX2_std_logic_vector) return integer is + | begin + | return A'length * bitWidth(A(0)); + | end; + | function to_slv(A : t_vecX2_std_logic_vector) return std_logic_vector is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer; + | variable ret : std_logic_vector(bitWidth(A) - 1 downto 0); + | begin + | cellBitWidth := bitWidth(A(0)); + | lo := bitWidth(A); + | for i in 0 to A'length-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(hi downto lo) := to_slv(A(i)); + | end loop; + | return ret; + | end; + | function to_t_vecX2_std_logic_vector(A : std_logic_vector; D2 : integer; D1 : integer; D0 : integer) return t_vecX2_std_logic_vector is + | variable hi : integer; + | variable lo : integer; + | variable cellBitWidth: integer := D1 * D0; + | variable ret : t_vecX2_std_logic_vector(0 to D2 - 1)(0 to D1 - 1)(D0 - 1 downto 0); + | begin + | lo := A'length; + | for i in 0 to D2-1 loop + | hi := lo - 1; lo := hi - cellBitWidth + 1; + | ret(i) := to_t_vecX1_std_logic_vector(A(hi downto lo), D1, D0); + | end loop; + | return ret; + | end; + | subtype t_opaque_Foo is t_vecX2_std_logic_vector(0 to 10 - 1)(0 to 16 - 1)(11 downto 0); + | function to_t_opaque_Foo(A : std_logic_vector) return t_opaque_Foo is + | begin + | return to_t_vecX2_std_logic_vector(A, 10, 16, 12); + | end; + | signal v : t_opaque_Foo; + | signal v_din : t_opaque_Foo; + |begin + | process (all) + | begin + | v_din <= v; + | v_din <= to_t_vecX2_std_logic_vector(x, 10, 16, 12); + | y <= to_slv(v); + | end process; + | process (clk) + | begin + | if rising_edge(clk) then + | if rst = '1' then v <= (0 to 10-1 => (0 to 16-1 => x"000")); + | else v <= v_din; + | end if; + | end if; + | end process; + |end Example_arch;""".stripMargin + ) + } + test("Vector local no conversion example") { + class Example() extends RTDesign: + val v = Bits(12) X 16 X 10 <> OUT + v := all(all(all(0))) + + val top = (Example()).getCompiledCodeString + assertNoDiff( + top, + """|library ieee; + |use ieee.std_logic_1164.all; + |use ieee.numeric_std.all; + |use work.Example_pkg.all; + | + |entity Example is + |port ( + | v : out t_vecX2_std_logic_vector(0 to 10 - 1)(0 to 16 - 1)(11 downto 0) + |); + |end Example; + | + |architecture Example_arch of Example is + |begin + | process (all) + | begin + | v <= (0 to 10-1 => (0 to 16-1 => x"000")); + | end process; + |end Example_arch; + |""".stripMargin + ) + } + test("Opaque and vector global example") { + case class Foo() extends Opaque(Bits(12) X 16 X 10) + class Example() extends RTDesign: + val x = Bits(1920) <> IN + val y = Foo <> OUT.REG init all(all(all(0))).as(Foo) + y.din := x.as(Foo) + + val top = (Example()).getCompiledCodeString + // TODO: consider if we want to leave the t_opaque_Foo under `getCompiledCodeString` + assertNoDiff( + top, + """|subtype t_opaque_Foo is t_vecX2_std_logic_vector(0 to 10 - 1)(0 to 16 - 1)(11 downto 0); + | + |library ieee; + |use ieee.std_logic_1164.all; + |use ieee.numeric_std.all; + |use work.Example_pkg.all; + | + |entity Example is + |port ( + | clk : in std_logic; + | rst : in std_logic; + | x : in std_logic_vector(1919 downto 0); + | y : out t_opaque_Foo + |); + |end Example; + | + |architecture Example_arch of Example is + | + | subtype t_opaque_Foo is t_vecX2_std_logic_vector(0 to 10 - 1)(0 to 16 - 1)(11 downto 0); + | function to_t_opaque_Foo(A : std_logic_vector) return t_opaque_Foo is + | begin + | return to_t_vecX2_std_logic_vector(A, 10, 16, 12); + | end; + | signal y_din : t_opaque_Foo; + |begin + | process (all) + | begin + | y_din <= y; + | y_din <= to_t_vecX2_std_logic_vector(x, 10, 16, 12); + | end process; + | process (clk) + | begin + | if rising_edge(clk) then + | if rst = '1' then y <= (0 to 10-1 => (0 to 16-1 => x"000")); + | else y <= y_din; + | end if; + | end if; + | end process; + |end Example_arch; + |""".stripMargin + ) + } end PrintVHDLCodeSpec diff --git a/lib/src/test/scala/issues/IssueSpec.scala b/lib/src/test/scala/issues/IssueSpec.scala index 3fac712c5..e19ced1ff 100644 --- a/lib/src/test/scala/issues/IssueSpec.scala +++ b/lib/src/test/scala/issues/IssueSpec.scala @@ -73,6 +73,9 @@ class IssuesSpec extends FunSuite: |endmodule |""".stripMargin ) + test("i141 compiles and passes VHDL linting"): + given options.CompilerOptions.Backend = backends.vhdl.v2008 + i141.StructArrayIssue().compile.lint test("i142 compiles and passes VHDL linting"): given options.CompilerOptions.Backend = backends.vhdl i142.IntegerIndexingIssue().compile.lint diff --git a/lib/src/test/scala/issues/i141.scala b/lib/src/test/scala/issues/i141.scala new file mode 100644 index 000000000..221d2e6f9 --- /dev/null +++ b/lib/src/test/scala/issues/i141.scala @@ -0,0 +1,13 @@ +// format: off +package issues.i141 + +import dfhdl._ + +case class EmbeddedArray ( + a : Bits[8]X(3) <> VAL +) extends Struct + +class StructArrayIssue() extends RTDesign: + val a = Bit <> IN + val e = EmbeddedArray <> VAR + e.a(0)(0) := a \ No newline at end of file