diff --git a/.claude/commands/bugfix.md b/.claude/commands/bugfix.md index 0c7091b86..55e8874f4 100644 --- a/.claude/commands/bugfix.md +++ b/.claude/commands/bugfix.md @@ -415,6 +415,20 @@ width as ONE named operation whose body does the whole calculation in `compileti Naming those operations (`CLog2P1`, `ArithMaxWidth`, `PartSelectHigh`, `RangeWidth`) is worth doing for its own sake, and it makes the guard-once rule visible at each site. +Even the single named fold has one context it cannot survive: a `Check2.CheckNUB[HI, HighIdx[W, +L]]` bound routes the application through `UBound.Aux` resolution, where the guard's reduction is +CONTEXT-DEPENDENT — it can collapse to `Int` inside the implicit search while the same application +reduces to a literal at the summon site, so the found candidate's inferred type no longer conforms +to the required one and the user sees a raw given-mismatch dump (issue #488), or the op fails to +resolve at all. The tell: `Found: given_CheckNUB_Wide...[..., T2 = Int, ...]` against +`Required: ...CheckNUB[..., HighIdx[(8 : Int), (2 : Int)]]` — the fold collapsed on one side and +not the other. The robust spelling moves the literal-vs-wide decision from the guard to GIVEN +prioritization: a type class (`IntP.HighIdxOf`) whose high-priority instance computes the bound in +raw `compiletime.ops` over `Int & Singleton` args and whose low-priority instance answers `Int`, +summoned in a FIRST using group so the check in the second group receives a plain, already-decided +type parameter. This is also what let the three bits range-selection givens collapse into one: +the H-form existed only to dodge the fold inside `UBound`. + ### Weakening a type does not break values, it deletes diagnostics Making the type level say less is safe for the generated hardware, because the IR carries the real diff --git a/.claude/commands/verilog-to-dfhdl.md b/.claude/commands/verilog-to-dfhdl.md index 63f19924a..53507ec99 100644 --- a/.claude/commands/verilog-to-dfhdl.md +++ b/.claude/commands/verilog-to-dfhdl.md @@ -91,8 +91,15 @@ automatically (the emitted parent shows `child_i_clk = wb_clk`). Beyond the per- Use the top's names as the global default; override the internals per-module. - **A clock-only annotation removes the reset.** On an `RTDesign` *with* registers, annotating only `@hw.constraints.timing.clock(portName = "...")` (no reset annotation) suppresses the default - reset entirely; the register `init`s emit as **power-up only** (`logic r = 1'b0;`, no `if(rst)`). - This is how you port a no-reset module (pipeline, RAM). + reset entirely; the register `init`s emit as **power-up only** (an `initial` block, no `rst_l` port + and no `if(rst)` arm). This is how you define a domain with an init and no reset signal, i.e. how + you port a no-reset module (pipeline, RAM). It is *not* a bug, and it is silent, so when you + rename a clock port on a module that **does** reset, restate `@..reset` alongside it or the flop + quietly becomes reset-less: + ```scala + @timing.clock(portName = "rawclk") // alone: no reset + @timing.reset(mode = _.async, active = _.low, portName = "rst_l") // keep the reset + ``` - **Annotation ⇒ auto-reset.** A `@..reset` annotation synchronously resets every register with a real `init` to that init (`if (rst) r <= init;`). A Verilog `if(rst) r <= RESET_VAL` mux folds straight into `init RESET_VAL` - drop the explicit mux. @@ -113,6 +120,41 @@ automatically (the emitted parent shows `child_i_clk = wb_clk`). Beyond the per- `clk.actual` reads it as a `Bit` (needed to drive a derived clock from the root clock, as an ungated ICG does). What does collide is a *non-magnet* port whose name shadows the magnet's, so keep a data port off the names `clk`/`rst` ("Unsupported read-to-read connection"). +- **Renaming a child's clock port works, and the parent binds it correctly.** + `@timing.clock(portName = "rawclk")` on a child emits `input wire logic rawclk`, and the parent + connects **its own** clock to it (`assign f_rawclk = clk;`) because the magnet matches by domain, + not by name. This is how you port a cell whose clock input the baseline calls something else. + The domain propagates *down*, so a grandchild instantiated inside that cell is emitted with + `rawclk` too, while the same class instantiated from an ordinary `clk` design is emitted with + `clk`. + + Two traps come with it, both found on VeeR's `rvdff_fpga`: + - **Keep a renamed-clock design childless.** If it instantiates anything, the emission is invalid + SystemVerilog: duplicated `rst_l_0`/`rst_l_1` ports against a `.rst_l` connection, plus + `assign ..clk = clk;` - a hierarchical assign through the module *type* + name. It also surfaces at the parent as a bogus `Found multiple connections write to the same + variable/port _clk`, once the parent instantiates a same-domain sibling. Both faces + vanish when the cell has no children, so write the leaf logic directly instead of wrapping a + child. It elaborates clean either way, so **run the emitted files through slang** after + renaming a clock port. + - **`val clk = Clk <> IN` silently beats `portName`.** In a design annotated + `@timing.clock(portName = "rawclk")`, adding `val clk = Clk <> IN` makes *that* the domain + clock: `rawclk` disappears from the port list entirely and the registers clock on whatever the + parent wires to `clk`. No diagnostic. So a cell that takes two clock inputs and flops on the + *renamed* one must declare the other as a plain `Bit <> IN` - which is safe, and the magnet + does **not** claim it despite the name: + ```scala + @timing.clock(portName = "rawclk") // rvdff_fpga, FPGA arm + @timing.reset(mode = _.async, active = _.low, portName = "rst_l") + class rvdff_fpga(val WIDTH: Int <> CONST = 1) extends RTDesign: + val clk = Bit <> IN // the baseline's dead clock input + val dout = Bits(WIDTH) <> OUT.REG init all(0) + if (clken) dout.din := din // gold: rvdffs (.clk(rawclk), .en(clken), .*) + // parent emits: assign f_rawclk = clk; (root clock) assign f_clk = ; (dead input) + ``` ## Memories and `initFile` diff --git a/benchmarks b/benchmarks index 8c99bf94d..34bbd6342 160000 --- a/benchmarks +++ b/benchmarks @@ -1 +1 @@ -Subproject commit 8c99bf94dd4deb2ff98b074182ce727e0c7dba6d +Subproject commit 34bbd6342cbf2113073ed2440029a8209111ca87 diff --git a/build.sbt b/build.sbt index f760b3742..2bea16e2a 100755 --- a/build.sbt +++ b/build.sbt @@ -39,7 +39,7 @@ val oslibVersion = "0.11.8" val scallopVersion = "6.0.0" val upickleVersion = "4.4.3" val scalapptainerVersion = "0.5.4" -val factumVersion = "0.2.0" +val factumVersion = "0.3.0" inThisBuild( List( diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DB.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DB.scala index 350f68a8a..f6747e491 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DB.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DB.scala @@ -350,6 +350,62 @@ final case class DB private ( List(top -> List()) ).reverse + /** The base name (no extension) of the HDL file each design is emitted into. + * + * A file is a DECLARATION's HDL output, not a design's: designs group by their `dclMeta` + * namespace and position, so one `class Bar` that elaboration specialized into several distinct + * designs (`Bar_0`, `Bar_1`, ...) emits ONE `Bar` file holding all of them, while same-named + * declarations from elsewhere keep files of their own. The declaration's name is recovered from + * the emitted names of the designs sharing it: their longest common prefix, cut back to its last + * `_`, which is the separator every duplication suffix is appended behind (`Bar_0`/`Bar_1` -> + * `Bar`, `Adder_8_0`/`Adder_8_1` -> `Adder_8`, and the instance-named clones `ReduplicateDesign` + * makes, `Foo_a`/`Foo_b` -> `Foo`). Reading the suffix off the GROUP rather than off one name is + * what tells an enumerated `Bar_0` apart from a declared `Adder_8`: a design that is the only + * one of its declaration was never suffixed, and keeps its whole name. + * + * Names are then made unique CASE-INSENSITIVELY (these are file names, and the output must be + * the same on every operating system), earlier designs winning: a lone design keeps its emitted + * name, which `UniqueDesigns` already made case-insensitively unique, so the fallback is what a + * recovered declaration name colliding with one of those takes (a `cipher` sub-design under a + * `Cipher` top stays in `cipher_0`, alongside `Cipher`). + * + * Shared by the emission (`Printer.designFileGroups`) and by anything that has to name the file + * a design ended up in, such as Verilator's per-file lint waivers. + */ + lazy val designFileNameMap: Map[DFDesignBlock, String] = + val byDeclaration = designMemberList.map(_._1).groupByOrdered { d => + // an UNKNOWN position (a synthesized design, declared in no Scala source) identifies no + // declaration, so such a design groups only with itself + if (d.dclMeta.position.isUnknown) Left(d) + else Right((d.dclMeta.namespace, d.dclMeta.position)) + } + val taken = mutable.Set.empty[String] + byDeclaration.flatMap { (_, designs) => + val preferred = + if (designs.sizeIs > 1) commonDclNameStem(designs) else designs.head.dclName + var fileName = preferred + var idx = 0 + while (!taken.add(fileName.toLowerCase)) + fileName = s"${preferred}_$idx" + idx += 1 + designs.map(_ -> fileName) + }.toMap + end designFileNameMap + + // The stem the emitted names of `designs` were all suffixed from: their longest common prefix, + // cut back to its last `_` (the separator a duplication suffix is appended behind) and stripped + // of it. Falls back to the first name whole where there is no such prefix, i.e. where the names + // do not actually share a suffixed stem. + private def commonDclNameStem(designs: List[DFDesignBlock]): String = + val first = designs.head.dclName + val prefixLen = designs.view.map(_.dclName).foldLeft(first.length) { (len, name) => + var i = 0 + while (i < len && i < name.length && first(i) == name(i)) i += 1 + i + } + val sepIdx = first.take(prefixLen).lastIndexOf('_') + if (sepIdx > 0) first.take(sepIdx) else first + // holds a hash table that lists members of each owner block. The member list order is maintained. lazy val designMemberTable: Map[DFDesignBlock, List[DFMember]] = Map(designMemberList*) @@ -2515,6 +2571,19 @@ final case class DB private ( if (newMembers == members) this else this.update(members = newMembers) end canonicalForm + // Whether every elaboration-loaded external init file (`SourceType.InitFile`, recorded with its + // loaded contents by `initFile`), here and in the sub-DBs, still reads back the contents this DB + // was elaborated with. The re-read goes through the same resolution elaboration used (classpath + // resource first, filesystem path second), and a missing or unreadable file counts as changed. + // Elaboration caches consult this to reject a stale entry: the design load gate before adopting + // a sub-design entry, and the DFApp elaborate step before accepting a whole-design cache hit. + def initFilesUnchanged: Boolean = + (srcFiles.view ++ subDBs.valuesIterator.flatMap(_.srcFiles)).forall { + case SourceFile(SourceOrigin.External, SourceType.InitFile, path, contents) => + InitFileFormat.readInitFileContentsOpt(path).contains(contents) + case _ => true + } + end DB object DB: diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala index 607ab61f2..72bf71876 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala @@ -17,13 +17,13 @@ enum InitFileUndefinedValue derives CanEqual, ReadWriter: object InitFileFormat: import InitFileFormat.* - def readInitFile( - fileName: String, - fileFormat: InitFileFormat, - arrLen: Int, - dataWidth: Int, - undefinedValue: InitFileUndefinedValue - ): Vector[(BitVector, BitVector)] = + + /** Reads the raw contents of an init file, resolved as a classpath resource first and a + * filesystem path second (line endings normalized to `\n`). This resolution is the identity of + * the file: cache validation re-reads through it and compares contents (see + * `readInitFileContentsOpt`). + */ + def readInitFileContents(fileName: String): String = val source = try Source.fromResource(fileName) catch @@ -34,8 +34,29 @@ object InitFileFormat: throw new IllegalArgumentException( s"Init file not found: $fileName\nmake sure either to place the file in your Scala project resource folder or provide a proper relative/absolute path." ) + try source.getLines().mkString("\n") + finally source.close() + end readInitFileContents + + /** As `readInitFileContents`, but None for a file that cannot be found or read. Serves cache + * validation, where an unreadable file means a stale entry (a miss) rather than an error: the + * live elaboration that follows raises the proper user-facing error. + */ + def readInitFileContentsOpt(fileName: String): Option[String] = + try Some(readInitFileContents(fileName)) + catch case scala.util.control.NonFatal(_) => None - val fileContents = source.getLines().mkString("\n") + /** Parses already-read init file contents (see `readInitFileContents`; `fileName` is for error + * reporting and `Auto` format detection only). + */ + def parseInitFile( + fileName: String, + fileContents: String, + fileFormat: InitFileFormat, + arrLen: Int, + dataWidth: Int, + undefinedValue: InitFileUndefinedValue + ): Vector[(BitVector, BitVector)] = val detectedFormat = fileFormat match case Auto => detectAutoFormat(fileName, fileContents, dataWidth) case _ => fileFormat @@ -55,7 +76,18 @@ object InitFileFormat: s"Init file error detected in $detectedFormat formatted ${fileName}:$lineNum\n$msg" ) end try - end readInitFile + end parseInitFile + + def readInitFile( + fileName: String, + fileFormat: InitFileFormat, + arrLen: Int, + dataWidth: Int, + undefinedValue: InitFileUndefinedValue + ): Vector[(BitVector, BitVector)] = + parseInitFile( + fileName, readInitFileContents(fileName), fileFormat, arrLen, dataWidth, undefinedValue + ) private val verilogCommentPattern = """//.*|/\*.*?\*/""".r private val validBinPattern = diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/SourceFile.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/SourceFile.scala index b2a3c0776..8863a47a8 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/SourceFile.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/SourceFile.scala @@ -14,6 +14,10 @@ enum SourceType derives CanEqual, ReadWriter: case BlackBox case GlobalDef case DFHDLDef + // An external data file loaded during elaboration (`initFile` memory contents), recorded with + // the loaded contents under `SourceOrigin.External`. Elaboration caches re-read the file and + // reject an entry whose file has since changed (see `DB.initFilesUnchanged`). + case InitFile case Tool(toolName: String, srcType: String) enum SourceOrigin derives CanEqual, ReadWriter: diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/SubDesignEntry.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/SubDesignEntry.scala index 3b381c0f6..8de3cac70 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/SubDesignEntry.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/SubDesignEntry.scala @@ -127,7 +127,11 @@ final case class SubDesignEntry( db.members.map(memberMap), newRefTable.view.mapValues(t => memberMap.getOrElse(t, t)).toMap, db.globalTags, - Nil + // the entry's recorded source files (external init files) stay with the adopted design: + // the final assembly emits them as the design's sub-DB content, so a whole-design cache + // (the DFApp elaborate step) can re-validate them even when this design was never + // elaborated live in the storing run of THAT cache + db.srcFiles ) end cloneForAdoption end SubDesignEntry diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/printing/Printer.scala b/compiler/ir/src/main/scala/dfhdl/compiler/printing/Printer.scala index b6d7d35e3..41adb80c4 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/printing/Printer.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/printing/Printer.scala @@ -661,22 +661,22 @@ trait Printer dfhdlSourceFile, packageSourceFiles, globalSourceFile, - designPrinters.view + designFileGroups( // A foreign IP supplies its own HDL wrapper as a bundled resource (copied into the project // at commit), so DFHDL must not generate an HDL file for it (that would duplicate the // wrapper module/entity). - .filterNot { case (block, _) => block.isForeignIPBlackbox } - .map { case (block, p) => - val sourceType = block.instMode match - case _: DFDesignBlock.InstMode.BlackBox => SourceType.BlackBox - case _ => SourceType.Design - SourceFile( - SourceOrigin.Compiled, - sourceType, - hdlFolderName + separatorChar + designFileName(block.dclName), - formatCode(p.csFile(block), withColor = false) - ) - } + designPrinters.filterNot { case (block, _) => block.isForeignIPBlackbox } + ).map { case (fileName, group) => + val sourceType = group.head._1.instMode match + case _: DFDesignBlock.InstMode.BlackBox => SourceType.BlackBox + case _ => SourceType.Design + SourceFile( + SourceOrigin.Compiled, + sourceType, + hdlFolderName + separatorChar + designFileName(fileName), + group.map((block, p) => formatCode(p.csFile(block), withColor = false)).mkString("\n") + ) + } ).flatten // removing existing compiled/committed files and adding the newly compiled files val srcFiles = designDB.srcFiles.filter { @@ -686,6 +686,52 @@ trait Printer designDB.update(srcFiles = srcFiles) end printedDB + /** The design FILES to emit, as `(file base name, the designs it holds)`. + * + * A file is a DECLARATION's HDL output, not a design's: see `DB.designFileNameMap`, which + * decides the grouping and the names. + * + * The groups come out in an order where a design's file precedes every file instantiating it, + * which VHDL analysis and Verilog compilation order both need. `designPrinters` already + * satisfies this design-by-design (post-order), but grouping merges positions, so the order is + * re-derived as a stable topological sort over the groups. A reference cycle (mutually recursive + * declarations) is broken at its first back edge, keeping the input order there. + */ + protected final def designFileGroups( + entries: List[(DFDesignBlock, TPrinter)] + ): List[(String, List[(DFDesignBlock, TPrinter)])] = + val designDB = getSet.designDB + val fileNameOf = designDB.designFileNameMap + val groupList = + entries.groupByOrdered((block, _) => fileNameOf.getOrElse(block, block.dclName)).toVector + val groupIdxOf: Map[DFDesignBlock, Int] = + groupList.iterator.zipWithIndex.flatMap { (group, idx) => + group._2.iterator.map(_._1 -> idx) + }.toMap + // group -> the groups holding the designs its designs instantiate + val prereqs = Array.fill(groupList.length)(mutable.LinkedHashSet.empty[Int]) + designDB.designBlockOwnershipMap.foreach { (child, owners) => + groupIdxOf.get(child).foreach { childIdx => + owners.foreach { owner => + groupIdxOf.get(owner).foreach(ownerIdx => + if (ownerIdx != childIdx) prereqs(ownerIdx) += childIdx + ) + } + } + } + val ordered = mutable.ListBuffer.empty[Int] + // 0 = unvisited, 1 = on the current path (a back edge to it is the cycle break), 2 = emitted + val state = Array.fill(groupList.length)(0) + def visit(idx: Int): Unit = + if (state(idx) == 0) + state(idx) = 1 + prereqs(idx).foreach(visit) + state(idx) = 2 + ordered.addOne(idx) + groupList.indices.foreach(visit) + ordered.toList.map(groupList(_)) + end designFileGroups + val printVendorIPBlackbox: Boolean = false // The (design block, printer-bound-to-its-getSet) pairs to render, in order. diff --git a/compiler/stages/src/test/scala/StagesSpec/NameVarVersionsSpec.scala b/compiler/stages/src/test/scala/StagesSpec/NameVarVersionsSpec.scala index ec55ae1b4..f8d9c272d 100644 --- a/compiler/stages/src/test/scala/StagesSpec/NameVarVersionsSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/NameVarVersionsSpec.scala @@ -293,8 +293,7 @@ class NameVarVersionsSpec extends StageSpec: | val v = Bits(8) <> VAR | v := x | c := x(0) - | val a: Bits[8] <> CONST = h"00" - | y2 := a + | y2 := h"00" | if (c) y2 := x | else if (x(1)) q.din := v | v := v | h"0f" @@ -331,9 +330,8 @@ class NameVarVersionsSpec extends StageSpec: | v := x | v_ver1 := v | report(s"v is ${v_ver1}", Severity.Warning) - | val a: Bits[8] <> CONST = h"05" | v_ver2 := v - | assert(v_ver2 == a, s"bad ${v_ver2}") + | assert(v_ver2 == h"05", s"bad ${v_ver2}") | v := v | h"0f" | y := v | end a diff --git a/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala b/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala index efce7f1dc..eb5573383 100644 --- a/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala @@ -4099,4 +4099,37 @@ class PrintCodeStringSpec extends StageSpec(stageCreatesUnrefAnons = true): |end Repro |""".stripMargin ) + // The `b`/`h` interpolators expand to `StrInterp.inline$interpolate`, an inline accessor the + // compiler mints so the expansion can reach a member it cannot name directly. + // `MetaContextGenPhase` used to read the `$` in that name as "compiler-generated" and let the + // constant keep the context propagated into it, so an anonymous interpolated constant inside a + // sub-design came out carrying the instance's own name from the parent design. + test("An anonymous interpolated constant in a sub-design does not take the instance's name"): + class Sub extends RTDesign: + val corners = Bits(4) <> IN + val hit = Bit <> OUT + hit := corners == b"4'1001" + class Top extends RTDesign: + val i = Bits(4) <> IN + val o = Bit <> OUT + val movecircle = new Sub + movecircle.corners <> i + o <> movecircle.hit + assertCodeString( + new Top, + """|class Sub extends RTDesign: + | val corners = Bits(4) <> IN + | val hit = Bit <> OUT + | hit := (corners == h"9").bit + |end Sub + | + |class Top extends RTDesign: + | val i = Bits(4) <> IN + | val o = Bit <> OUT + | val movecircle = Sub() + | movecircle.corners <> i + | o <> movecircle.hit + |end Top + |""".stripMargin + ) end PrintCodeStringSpec diff --git a/compiler/stages/src/test/scala/StagesSpec/SubDesignCacheSpec.scala b/compiler/stages/src/test/scala/StagesSpec/SubDesignCacheSpec.scala index f0fba5ba0..8f3ab9614 100644 --- a/compiler/stages/src/test/scala/StagesSpec/SubDesignCacheSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/SubDesignCacheSpec.scala @@ -22,6 +22,16 @@ def topCalcB(arg: UInt[8] <> VAL): UInt[8] <> DFRET = (arg - 4) * 5 val globalW: UInt[8] <> CONST = 5 def topCalcG(arg: UInt[8] <> VAL): UInt[8] <> DFRET = arg + globalW +// Counts def body elaborations for the init-file staleness test. A top-level (static) object is +// not a capture, so it stays out of the design load key (whose `localKey` folds every capture's +// `toString`, which for a counter would change per run) and only observes what actually ran. The +// count lives in a Java atomic because a Scala `var` write is an effect the purity analysis sees, +// which would make the counted design impure and unkeyable (see `ClassBodyElaborations`). +object InitFileBodyElaborations: + private val n = java.util.concurrent.atomic.AtomicInteger(0) + def tick(): Unit = n.incrementAndGet() + def count: Int = n.get() + /** Tests for the sub-design cache tier of the elaboration design load gate * (`ElaborationOptions.CacheEnable`): a pure method whose cached DB is found by the * `SubDesignDiskCache` service skips its body elaboration entirely; the harness still creates the @@ -516,6 +526,73 @@ class SubDesignCacheSpec extends StageSpec(stageCreatesUnrefAnons = true): assertEquals(cache.hits, 1) } + // A def whose body loads an init file: the file is an elaboration input the design load key + // cannot carry (only running the body discovers which files it reads), so the entry records the + // path and the loaded contents as a `SourceType.InitFile` source file, and the gate re-reads the + // file on lookup (`DB.initFilesUnchanged`). An unchanged file hits and skips the body; a changed + // file rejects the entry, the body re-elaborates live, and the fresh entry (with the new + // contents baked into the init constant) overwrites the same key. + test("an entry whose init file changed is rejected and re-elaborated live") { + val initFile = java.nio.file.Files.createTempFile("dfhdl-initfile-spec", ".hex") + try + val path = initFile.toString + def genInitHost(using DFC): dfhdl.core.Design = + class InitHost extends DFDesign: + val idx = UInt(2) <> IN + val o = Bits(8) <> OUT + def memRead(i: UInt[2] <> VAL): Bits[8] <> DFRET = + InitFileBodyElaborations.tick() + val mem = Bits(8) X 4 <> VAR initFile path + mem(i) + o := memRead(idx) + end InitHost + new InitHost + def expectedInit(cells: String) = + s"""|def memRead(i: UInt[2] <> VAL): Bits[8] <> DFRET = + | val mem = Bits(8) X 4 <> VAR init DFVector(Bits(8) X 4)($cells) + | mem(i.toInt) + |end memRead + | + |class InitHost extends DFDesign: + | val idx = UInt(2) <> IN + | val o = Bits(8) <> OUT + | o := memRead(idx) + |end InitHost + |""".stripMargin + val cache = new MapSubDesignCache + val runs0 = InitFileBodyElaborations.count + java.nio.file.Files.writeString(initFile, "18\n24\n42\n81") + assertCodeString( + genHostOf(genInitHost, cache), + expectedInit("""h"18", h"24", h"42", h"81"""") + ) + assertEquals(InitFileBodyElaborations.count, runs0 + 1) + assertEquals(cache.entries.size, 1) + // unchanged file: the entry validates and the body elaboration is skipped + assertCodeString( + genHostOf(genInitHost, cache), + expectedInit("""h"18", h"24", h"42", h"81"""") + ) + assertEquals(InitFileBodyElaborations.count, runs0 + 1) + // changed file: the entry's recorded contents are stale, so it is rejected and the body + // runs live, baking the NEW contents and overwriting the entry under the same key + java.nio.file.Files.writeString(initFile, "01\n02\n03\n04") + assertCodeString( + genHostOf(genInitHost, cache), + expectedInit("""h"01", h"02", h"03", h"04"""") + ) + assertEquals(InitFileBodyElaborations.count, runs0 + 2) + assertEquals(cache.entries.size, 1) + // the overwritten entry now validates against the new contents and hits again + assertCodeString( + genHostOf(genInitHost, cache), + expectedInit("""h"01", h"02", h"03", h"04"""") + ) + assertEquals(InitFileBodyElaborations.count, runs0 + 2) + finally java.nio.file.Files.deleteIfExists(initFile) + end try + } + test("without cacheEnable the elaboration is unaffected") { assertCodeString(genHost(using liveDFC), expectedCodeString) // the live dropped view matches the cached one asserted above diff --git a/compiler/stages/src/test/scala/StagesSpec/ToEDSpec.scala b/compiler/stages/src/test/scala/StagesSpec/ToEDSpec.scala index eaa96230d..c55a581ca 100644 --- a/compiler/stages/src/test/scala/StagesSpec/ToEDSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/ToEDSpec.scala @@ -158,11 +158,9 @@ class ToEDSpec extends StageSpec(stageCreatesUnrefAnons = true): | val we = Bit <> IN | val status = Bits(2) <> OUT | val q = Bits(8) <> OUT - | val a_0: Bits[2] <> CONST = b"00" - | val a_1: Bits[2] <> CONST = b"11" | process(all): - | status := a_0 - | if (we) status := a_1 + | status := b"00" + | if (we) status := b"11" | process(clk): | if (clk.actual.rising) | if (we) ram(addr.uint.toInt) :== data diff --git a/core/src/main/scala/dfhdl/core/DFBits.scala b/core/src/main/scala/dfhdl/core/DFBits.scala index ad8dd235f..5c2b0d5b7 100644 --- a/core/src/main/scala/dfhdl/core/DFBits.scala +++ b/core/src/main/scala/dfhdl/core/DFBits.scala @@ -705,49 +705,6 @@ object DFBits: DFVal.Alias.ApplyIdx(DFBit, lhs, idxVal) }(using dfc, CTName("bit selection (apply)")) end evOpApplyDFBitsWL - given evOpApplyRangeDFBitsWL[ - W <: IntP, - L2 <: IntP, - A, - C, - I, - P, - L <: DFVal[DFBitsWL[W, L2], Modifier[A, C, I, P]], - HI <: IntP, - LO <: IntP - ](using - checkHigh: BitIndexHigh.CheckNUB[HI, IntP.HighIdx[W, L2]], - checkLow: BitIndexLow.CheckNUB[LO, L2], - checkHiLo: BitsHiLo.CheckNUB[HI, LO] - ): ExactOp3Aux["apply", DFC, DFValAny, L, HI, LO, DFVal[ - DFBits[IntP.RangeWidth[HI, LO]], - Modifier[A, C, Any, P] - ]] = - new ExactOp3["apply", DFC, DFValAny, L, HI, LO]: - type Out = DFVal[DFBits[IntP.RangeWidth[HI, LO]], Modifier[A, C, Any, P]] - def apply(lhs: L, idxHigh: HI, idxLow: LO)(using DFC): Out = trydf { - import dfc.getSet - val idxHighParam = IntParam(idxHigh) - val idxLowParam = IntParam(idxLow) - val idxHighIntOpt = idxHighParam.toScalaIntOpt - val idxLowIntOpt = idxLowParam.toScalaIntOpt - val dfTypeIR = lhs.asIR.dfType.asInstanceOf[ir.DFBitsWL] - val lowIntOpt = dfTypeIR.lowIdxIntOpt - val highIntOpt = (dfTypeIR.widthIntOpt, lowIntOpt) match - case (Some(widthInt), Some(lowInt)) => Some(lowInt + widthInt - 1) - case _ => None - (idxHighIntOpt, highIntOpt) match - case (Some(idxHighInt), Some(highInt)) => checkHigh(idxHighInt, highInt) - case _ => - (idxLowIntOpt, lowIntOpt) match - case (Some(idxLowInt), Some(lowInt)) => checkLow(idxLowInt, lowInt) - case _ => - (idxHighIntOpt, idxLowIntOpt) match - case (Some(idxHighInt), Some(idxLowInt)) => checkHiLo(idxHighInt, idxLowInt) - case _ => - DFVal.Alias.ApplyRange(lhs, idxHighParam, idxLowParam) - }(using dfc, CTName("bit range selection (apply)")) - end evOpApplyRangeDFBitsWL end OpsLP object Ops extends OpsLP: import IntP.{-, +} @@ -768,59 +725,25 @@ object DFBits: DFVal.Alias.ApplyIdx(DFBit, lhs, ub(lhs.widthIntParam, idx)(using dfc.anonymize)) }(using dfc, CTName("bit selection (apply)")) end evOpApplyDFBits + // one range-selection given serves every bits receiver: a plain `Bits[W]` is + // `DFBitsWL[W, 0]`, and a `BitsHL` receiver either reduces its width to a literal + // (literal bounds) or collapses it to `Int` (constant bounds). The high bound is + // computed by `HighIdxOf` given dispatch and fed to the check as the PLAIN type + // parameter `H`, never as a fold application (see `HighIdxOf` for why) given evOpApplyRangeDFBits[ W <: IntP, - A, - C, - I, - P, - L <: DFVal[DFBits[W], Modifier[A, C, I, P]], - HI <: IntP, - LO <: IntP - ](using - checkHigh: BitIndex.CheckNUB[HI, W], - checkLow: BitIndex.CheckNUB[LO, W], - checkHiLo: BitsHiLo.CheckNUB[HI, LO] - ): ExactOp3Aux["apply", DFC, DFValAny, L, HI, LO, DFVal[ - DFBits[IntP.RangeWidth[HI, LO]], - Modifier[A, C, Any, P] - ]] = - new ExactOp3["apply", DFC, DFValAny, L, HI, LO]: - type Out = DFVal[DFBits[IntP.RangeWidth[HI, LO]], Modifier[A, C, Any, P]] - def apply(lhs: L, idxHigh: HI, idxLow: LO)(using DFC): Out = trydf { - val idxHighParam = IntParam(idxHigh) - val idxLowParam = IntParam(idxLow) - val idxHighIntOpt = idxHighParam.toScalaIntOpt - val idxLowIntOpt = idxLowParam.toScalaIntOpt - val widthIntOpt = lhs.widthIntOpt - (idxHighIntOpt, widthIntOpt) match - case (Some(idxHighInt), Some(widthInt)) => checkHigh(idxHighInt, widthInt) - case _ => - (idxLowIntOpt, widthIntOpt) match - case (Some(idxLowInt), Some(widthInt)) => checkLow(idxLowInt, widthInt) - case _ => - (idxHighIntOpt, idxLowIntOpt) match - case (Some(idxHighInt), Some(idxLowInt)) => checkHiLo(idxHighInt, idxLowInt) - case _ => - DFVal.Alias.ApplyRange(lhs, idxHighParam, idxLowParam) - }(using dfc, CTName("bit range selection (apply)")) - end evOpApplyRangeDFBits - // the annotation path (a `BitsHL[9, 2] <> VAL` field or parameter) carries the width - // as the UNREDUCED `RangeWidth[H, L]` application, where the W-form's `HighIdx[W, L]` - // bound gets stuck (fold over a fold); binding `H` structurally checks on `H` directly. - // The term-construction path reduces the width to a literal, misses this pattern, and - // resolves to the W-form above instead. - given evOpApplyRangeDFBitsHL[ - H <: IntP, L2 <: IntP, + H <: IntP, A, C, I, P, - L <: DFVal[DFBitsHL[H, L2], Modifier[A, C, I, P]], + L <: DFVal[DFBitsWL[W, L2], Modifier[A, C, I, P]], HI <: IntP, LO <: IntP ](using + hIdx: IntP.HighIdxOf.Aux[W, L2, H] + )(using checkHigh: BitIndexHigh.CheckNUB[HI, H], checkLow: BitIndexLow.CheckNUB[LO, L2], checkHiLo: BitsHiLo.CheckNUB[HI, LO] @@ -852,7 +775,7 @@ object DFBits: case _ => DFVal.Alias.ApplyRange(lhs, idxHighParam, idxLowParam) }(using dfc, CTName("bit range selection (apply)")) - end evOpApplyRangeDFBitsHL + end evOpApplyRangeDFBits given evOpLogicDFBits[ Op <: FuncOp.|.type | FuncOp.&.type | FuncOp.^.type, L, diff --git a/core/src/main/scala/dfhdl/core/DFBoolOrBit.scala b/core/src/main/scala/dfhdl/core/DFBoolOrBit.scala index 1cc924d47..d20a1b4e2 100644 --- a/core/src/main/scala/dfhdl/core/DFBoolOrBit.scala +++ b/core/src/main/scala/dfhdl/core/DFBoolOrBit.scala @@ -6,7 +6,6 @@ import dfhdl.internals.* import annotation.{implicitNotFound, targetName} import scala.util.NotGiven -type BitNum = 0 | 1 type BitOrBool = BitNum | Boolean type DFBoolOrBit = DFType[ir.DFBoolOrBit, NoArgs] object DFBoolOrBit: @@ -345,28 +344,3 @@ given CanEqual[DFBoolOrBit, DFBoolOrBit] = CanEqual.derived type DFConstBool = DFConstOf[DFBool] type DFConstBit = DFConstOf[DFBit] -//BitNumWrapper is a wrapper for BitNum to preserve 0 or 1 values in basic operations -//The type is also used as `Bit` in the DFHDL frontend, to allow using BitNum values in DFHDL code -//and constructing DFBit DFHDL valeu types such as `Bit <> CONST`. -//TODO: implemented workaround for https://github.com/scala/scala3/issues/26550 -into sealed class BitNumWrapper(val value: Int) extends AnyVal derives CanEqual: - def unary_! : BitNumWrapper = BitNumWrapper(if value == 0 then 1 else 0) - def unary_~ : BitNumWrapper = unary_! - def |(rhs: BitNumWrapper): BitNumWrapper = - BitNumWrapper(if value == 1 || rhs.value == 1 then 1 else 0) - def &(rhs: BitNumWrapper): BitNumWrapper = - BitNumWrapper(if value == 1 && rhs.value == 1 then 1 else 0) - def ^(rhs: BitNumWrapper): BitNumWrapper = - BitNumWrapper(if value != rhs.value then 1 else 0) - def &&(rhs: BitNumWrapper): BitNumWrapper = this & rhs - def ||(rhs: BitNumWrapper): BitNumWrapper = this | rhs - def ==(rhs: BitNum): Boolean = value == rhs - def !=(rhs: BitNum): Boolean = value != rhs - -object BitNumWrapper: - def apply(value: BitNum): BitNumWrapper = new BitNumWrapper(value) - given [T <: Int & Singleton](using T <:< BitNum): Conversion[T, BitNumWrapper] = - x => BitNumWrapper(x.asInstanceOf[BitNum]) - given CanEqual[BitNumWrapper, BitNum] = CanEqual.derived - // TODO: implemented workaround for https://github.com/scala/scala3/issues/26550 - implicit def toBitNum(wrapper: BitNumWrapper): BitNum = wrapper.value.asInstanceOf[BitNum] diff --git a/core/src/main/scala/dfhdl/core/DFVal.scala b/core/src/main/scala/dfhdl/core/DFVal.scala index 411c505ea..7606a8cdd 100644 --- a/core/src/main/scala/dfhdl/core/DFVal.scala +++ b/core/src/main/scala/dfhdl/core/DFVal.scala @@ -690,8 +690,15 @@ object DFVal extends DFValLP: s"Vector cell type must have a known width to be initialized from a file." ) } - val data = ir.InitFileFormat.readInitFile( - path, format, length, width, undefinedValue + val contents = ir.InitFileFormat.readInitFileContents(path) + val data = ir.InitFileFormat.parseInitFile( + path, contents, format, length, width, undefinedValue + ) + // The loaded file is an elaboration input the design's cache key cannot carry, so it is + // recorded (path + loaded contents) as a source file of the current design. Elaboration + // caches re-read the file on a hit and reject a stale entry (`DB.initFilesUnchanged`). + dfc.mutableDB.DesignContext.addSrcFile( + ir.SourceFile(ir.SourceOrigin.External, ir.SourceType.InitFile, path, contents) ) val initFileConst = vectorType.cellType.asIR match case _: ir.DFBitsWL => DFVal.Const(vectorType, data) @@ -1425,8 +1432,6 @@ object DFVal extends DFValLP: evOpApplyDFBits, evOpApplyDFBitsWL, evOpApplyRangeDFBits, - evOpApplyRangeDFBitsWL, - evOpApplyRangeDFBitsHL, evOpAsDFBits, evOpLogicReduceDFBits, evOpShift diff --git a/core/src/main/scala/dfhdl/core/IntParam.scala b/core/src/main/scala/dfhdl/core/IntParam.scala index 16b027123..7b96883b9 100644 --- a/core/src/main/scala/dfhdl/core/IntParam.scala +++ b/core/src/main/scala/dfhdl/core/IntParam.scala @@ -226,6 +226,27 @@ object IntP: /** `BI - SW + 1`, the low index of a descending part-select anchored at `BI`. */ type PartSelectLow[BI <: IntP, SW <: IntP] = RangeWidth[BI, SW] + /** The high (absolute) index `W + L - 1` of a low-indexed bit vector, resolved by GIVEN dispatch + * rather than by the [[HighIdx]] guarded fold. A fold's const-guard reduction is + * context-dependent: inside a `UBound` resolution it can collapse to `Int` while the same + * application reduces to a literal at the summon site, poisoning the implicit search with a + * candidate whose inferred type no longer conforms to the required one (issue #488). Given + * prioritization makes the same literal-vs-wide decision robustly: the literal instance computes + * directly in `compiletime.ops`, and anything else falls to the wide `Int` instance, degrading + * the bound check to its elaboration-time half. + */ + sealed trait HighIdxOf[W <: IntP, L <: IntP]: + type Out <: IntP + protected sealed trait HighIdxOfLP: + protected val highIdxOfInstance: HighIdxOf[Int, Int] = new HighIdxOf[Int, Int] {} + given wide[W <: IntP, L <: IntP]: HighIdxOf.Aux[W, L, Int] = + highIdxOfInstance.asInstanceOf[HighIdxOf.Aux[W, L, Int]] + object HighIdxOf extends HighIdxOfLP: + type Aux[W <: IntP, L <: IntP, O <: IntP] = HighIdxOf[W, L] { type Out = O } + given literal[W <: Int & Singleton, L <: Int & Singleton] + : Aux[W, L, int.-[int.+[W, L], 1]] = + highIdxOfInstance.asInstanceOf[Aux[W, L, int.-[int.+[W, L], 1]]] + end IntP into opaque type IntParam[V <: IntP] = Int | DFConstInt32 diff --git a/core/src/main/scala/dfhdl/core/MutableDB.scala b/core/src/main/scala/dfhdl/core/MutableDB.scala index 57c2d45a0..29bcc83fb 100644 --- a/core/src/main/scala/dfhdl/core/MutableDB.scala +++ b/core/src/main/scala/dfhdl/core/MutableDB.scala @@ -42,6 +42,11 @@ private case class MemberEntry( class DesignContext: val members = mutable.ArrayBuffer.empty[MemberEntry] + // Source files this design's body loaded during elaboration (external init files, recorded + // with their loaded contents by `initFile`). Snapshotted per design at `endDesign` + // (`designSrcFiles`) and emitted on the design's own sub-DB, where elaboration caches + // re-validate them against the file system (see `DB.initFilesUnchanged`). + val srcFiles = mutable.ListBuffer.empty[SourceFile] val memberTable = mutable.Map.empty[DFMember, Int] val refTable = mutable.Map.empty[DFRefAny, DFMember] val originRefTable = mutable.Map.empty[DFRef.TwoWayAny, DFMember] @@ -322,6 +327,12 @@ final class MutableDB(): // ~~~ the designs of this run, keyed by identity (`refId`) and never by the block value ~~~ // the end-of-design member snapshot of a design, and the design block itself as it stood then val designMembers = mutable.Map.empty[StaticRef, List[DFMember]] + // the end-of-design snapshot of the source files the design's body loaded (see + // `DesignContext.srcFiles`) + val designSrcFiles = mutable.Map.empty[StaticRef, List[SourceFile]] + // records a source file the CURRENT design's body loaded (e.g. `initFile` registering the + // init file it read, together with the contents it read) + def addSrcFile(srcFile: SourceFile): Unit = current.srcFiles += srcFile private val designOf = mutable.Map.empty[StaticRef, DFDesignBlock] // the dclName groups feeding the emitted-name enumeration (`dclNameEnumeration`); the head of // a group is its canonical design @@ -393,6 +404,7 @@ final class MutableDB(): // designs wholesale (they are never `isLive`, their instances unify to the // canonical), so a duplicate's retained snapshot is simply never read. designMembers += design.refId -> currentMembers + designSrcFiles += design.refId -> current.srcFiles.distinct.toList stack.head.refTable ++= currentRefTable // origin lookups must survive the design's end just like regular ref lookups: the parent // may query the origin of a ref held by a child member (e.g. printing a child port's @@ -494,7 +506,15 @@ final class MutableDB(): // NOTE: the design block's transient elaboration-time instance cache is NOT cleared // here; the design is still live in this run (it is not serialized into an entry) val dbMembers = globalsClosure(c :: locals) ::: c :: locals - DB(dbMembers, refsFor(dbMembers), GlobalTagContext.tags, Nil) + // the design's own loaded source files (external init files) ride its sub-DB: into its + // cache entry, where the gate re-validates them on lookup, and into the final forest, + // where the DFApp elaborate step re-validates the whole design's set on a cache hit + DB( + dbMembers, + refsFor(dbMembers), + GlobalTagContext.tags, + designSrcFiles.getOrElse(c.refId, Nil) + ) end buildSubDB // ~~~ the run's design forest ~~~ @@ -619,6 +639,11 @@ final class MutableDB(): for cls <- classOf(childRef.ownerClassName, loader) entry <- subDesignCache.lookup(cls, childRef.localKey) + // stale-entry guard, same as the gate lookup's: a child entry whose recorded init + // files no longer match the file system fails the WHOLE adoption (children resolve + // before the parent commits), so the parent's `lookup` returns None and its body + // runs live, re-elaborating this child live as well + if entry.db.initFilesUnchanged design <- adopt(entry, childRef, loader) yield design } @@ -680,11 +705,15 @@ final class MutableDB(): subDesignCache.lookup(ownerClass, key.localKey) // guard against key collisions and stale entries: the stored design must be // the same declaration (name-insensitive: dclName enumeration may differ - // between the storing and loading runs); a mismatch is a miss + // between the storing and loading runs); a mismatch is a miss. The entry's + // recorded init files must also still match the file system (the key cannot + // carry file contents, since only the body knows which files it reads); a + // changed or missing file is a miss and the body re-elaborates live .filter { entry => val stored = entry.db.top stored.instMode == shell.instMode && stored.domainType == shell.domainType && - stored.dclMeta.position == shell.dclMeta.position + stored.dclMeta.position == shell.dclMeta.position && + entry.db.initFilesUnchanged } .flatMap(adopt(_, ref, ownerClass.getClassLoader)) .map { adoptedDesign => @@ -1193,7 +1222,7 @@ final class MutableDB(): case d: DFDesignBlock if d eq naturalTop => dFinal case m => fixedMember(m) } - DB(fixedMembers, refsFor(dFinal, fixedMembers), globalTags, Nil) + DB(fixedMembers, refsFor(dFinal, fixedMembers), globalTags, sub.srcFiles) // fix one ADOPTED sub-DB: its refs are self-contained (they were cloned onto this run's // tokens at adoption, resolving within the sub-DB), so only the design block itself is // renamed here, wherever it appears @@ -1206,7 +1235,7 @@ final class MutableDB(): val newRefTable = sub.refTable.view.mapValues { t => if (t eq (adoptedTop: DFMember)) (dFinal: DFMember) else t }.toMap - DB(newMembers, newRefTable, globalTags, Nil) + DB(newMembers, newRefTable, globalTags, sub.srcFiles) // ~~~ apply the fixes over the natural forest, sub-DB by sub-DB (in forest order) ~~~ val builtSubDBs = mutable.LinkedHashMap.empty[StaticRef, DB] natural.subDBs.foreach { (key, sub) => diff --git a/core/src/main/scala/dfhdl/hdl.scala b/core/src/main/scala/dfhdl/hdl.scala index f838d9664..7cfcbb600 100644 --- a/core/src/main/scala/dfhdl/hdl.scala +++ b/core/src/main/scala/dfhdl/hdl.scala @@ -52,7 +52,7 @@ object __hdl: export internals.Inlined type DFType = core.DFTypeAny lazy val Bit = core.DFBit - type Bit = core.BitNumWrapper + type Bit = internals.BitNumWrapper type Bits[W <: IntP] = core.DFBits[W] val Bits = core.DFBits type BitsHL[H <: IntP, L <: IntP] = core.DFBitsHL[H, L] diff --git a/core/src/test/scala/CoreSpec/DFBitsSpec.scala b/core/src/test/scala/CoreSpec/DFBitsSpec.scala index e61e97a9c..b3691c6d5 100644 --- a/core/src/test/scala/CoreSpec/DFBitsSpec.scala +++ b/core/src/test/scala/CoreSpec/DFBitsSpec.scala @@ -617,4 +617,16 @@ class DFBitsSpec extends DFSpec: o := p.x(HI, LO) } } + test("BitsHL struct field selection reports the range check message (#488)") { + case class P(f: BitsHL[9, 2] <> VAL, g: Bit <> VAL) extends Struct + val p = P <> VAR + val fifteen = 15 + assertDSLErrorLog( + "Index 15 is above the high index 9 of the selected value" + )( + """p.f(15, 12)""" + ) { + p.f(fifteen, 12) + } + } end DFBitsSpec diff --git a/core/src/test/scala/CoreSpec/DFVectorSpec.scala b/core/src/test/scala/CoreSpec/DFVectorSpec.scala index 5d055a270..7b68f1fba 100644 --- a/core/src/test/scala/CoreSpec/DFVectorSpec.scala +++ b/core/src/test/scala/CoreSpec/DFVectorSpec.scala @@ -162,6 +162,31 @@ class DFVectorSpec extends DFSpec: } } } + // Scala widens the element type of a collection of literals to `Int`, which `Exact` + // restores to `BitNum` when every literal is a 0 or 1 (see `Exact.asBitCollection`), + // so that such a collection can be applied to a `Bit` vector. A collection headed + // elsewhere is unaffected. + test("Bit vector from Int literals") { + assertCodeString( + """|val v1 = Bit X 4 <> VAR init DFVector(Bit X 4)(1, 1, 1, 1) + |v1 := DFVector(Bit X 4)(0, 1, 0, 1) + |v1 := DFVector(Bit X 4)(0, 0, 0, 0) + |val v2 = UInt(8) X 4 <> VAR + |v2 := DFVector(UInt(8) X 4)(d"8'0", d"8'1", d"8'0", d"8'1") + |v2 := DFVector(UInt(8) X 4)(d"8'1", d"8'1", d"8'1", d"8'1") + |v2 := DFVector(UInt(8) X 4)(d"8'1", d"8'2", d"8'3", d"8'4") + |""".stripMargin + ) { + val v1 = Bit X 4 <> VAR init Vector.fill(4)(1) + v1 := Vector(0, 1, 0, 1) + v1 := List.fill(4)(0) + val v2 = UInt(8) X 4 <> VAR + v2 := Vector(0, 1, 0, 1) + v2 := Vector.fill(4)(1) + v2 := Vector(1, 2, 3, 4) + } + } + test("Big Endian Packed Order") { val v: Bits[8] X 4 <> CONST = Vector(h"12", h"34", h"56", h"78") val v2: Bits[8] X Int <> CONST = Vector(h"12", h"34", h"56", h"78") diff --git a/devdocs/elaboration-caching.md b/devdocs/elaboration-caching.md index e5a9aa64f..e568dedcc 100644 --- a/devdocs/elaboration-caching.md +++ b/devdocs/elaboration-caching.md @@ -47,6 +47,11 @@ A design is loadable only if its body is *pure*: its structure is a function of else. Elaboration-time reads of mutable state, of the wall clock, or of a design parameter's *data* all make the body depend on something the key does not carry. +One trusted-frontend read is deliberately NOT an impurity: `initFile`'s file load. The file's +contents are an elaboration input like any other, but only the body knows which files it reads, so +they cannot join the key; they are recorded on the design instead and re-checked on every cache +hit. See [External init files](#external-init-files). + `PureCheckPhase` (compiler plugin) analyzes every design and records the verdict on the design's `dclMeta` as `@hw.annotation.pure`. Designs are pure by default; the phase escalates to `pure(false)` when it sees an effect it cannot attribute. The interesting middle case is *data impurity*: a body @@ -294,7 +299,63 @@ same `CodeDigest`, falling back to a runtime `factum.CodeRef` walk for an entry saw), the DFHDL version, the default RT domain config, and the design's arguments. A hit prints `Loading elaborated design from cache...` and never forces the top constructor thunk. It is enabled by `AppOptions.cacheEnable`, and is strictly coarser than the gate: it replays a whole design, or -nothing. +nothing. A hit additionally re-validates the init files the cached elaboration loaded (see +[External init files](#external-init-files)); a stale file re-elaborates and overwrites the entry. + +## External init files + +`initFile` reads a memory-init file during elaboration and bakes its data into a `Const`. That +makes the file's CONTENTS an elaboration input, and one no cache key can carry: the gate keys a +design BEFORE its body runs, and only the body knows which files it reads (the path can be +computed). The alternative of escalating the read to an impurity would kill caching for the design +and its whole subtree, for an input that is perfectly legitimate cache material. So the dependence +is tracked as a recorded effect and re-checked on every hit instead, the first realized instance of +the tracked-effect direction in the improvement notes below. + +**Recording.** When `initFile` runs, it registers the file on the CURRENT design as a +`SourceFile(External, InitFile, path, contents)`, where `contents` is exactly what it parsed +(`MutableDB.DesignContext.addSrcFile`). The design's end-of-design snapshot keeps them +(`designSrcFiles`), and `buildDesignSubDB` emits them as the sub-DB's `srcFiles`, so they travel +wherever the design's DB travels: into its `SubDesignEntry`, through adoption +(`cloneForAdoption` preserves them), through the final assembly's fix passes, and into the +hierarchical DB the DFApp elaborate step serializes. `SourceOrigin.External` has no other +consumer today (tools and commit filter on `Committed`), so the records never leak into emitted +file lists. + +**Validation** is `DB.initFilesUnchanged`: re-read every recorded path through the SAME resolution +elaboration used (classpath resource first, filesystem second; `readInitFileContentsOpt`) and +compare contents. A missing or unreadable file counts as changed, which is a MISS and not an +error: the live elaboration that follows raises the proper user-facing error if the file is truly +gone. Storing contents rather than a hash keeps the check exact with no separate bookkeeping, and +positions the record for the deferred-read future (a backend emitting `$readmemh` needs the file +beside the HDL; the TODO at `initFile` in `core.DFVal`). The cost is the file text stored verbatim +in the entry JSON. + +**At the gate**, validation runs where an entry is accepted, once per entry and BEFORE adoption: +in `DesignLoadGate.lookup`'s stale-entry filter, and in `childDesignOf` when a cached parent's +child is resolved. It covers every service tier, memory and disk alike (a file edited between two +runs of one sbt session is exactly the dev loop this exists for), and every service implementation, +including test fakes. No upward propagation into parent entries is needed: children resolve before +a parent's adoption commits, so a stale CHILD entry fails `childDesignOf`, which fails the parent's +whole adoption, and the parent's `lookup` returns None and its body (and the child's) re-elaborates +live. The fresh entries then overwrite the same keys, since the key deliberately excludes the +contents: a changed file replaces the entry rather than accumulating one dead entry per historical +version. The intra-run tier needs no check at all (a file changing mid-elaboration is not a +supported scenario). + +**At the DFApp elaborate step**, the same walk runs on a step-cache hit, over the deserialized +hierarchical DB's sub-DBs (each carries its own files, adopted designs included). The hook is +Factum's hit-validation (`Task.cached`'s `validate`, factum >= 0.3.0, surfaced as +`DiskCache.Step.cacheHitValidator`): a rejected value logs +`An init file has changed; re-elaborating design...`, recomputes the step as a miss, and +overwrites the entry under the same action key, with downstream steps (compile, commit) re-keying +through the fresh value digest. The compile/commit steps need no validators of their own: they are +pure functions of the elaborate value. + +**Tests**: `SubDesignCacheSpec` "an entry whose init file changed is rejected and re-elaborated +live" (the gate tier, body-run counted through a static-object atomic, since a captured counter +would destabilize the key through `localKey`'s capture `toString` fold), and +`internals.DiskCacheSpec` (the step-tier validator seam end to end through Factum). ## Working with the cache @@ -357,7 +418,9 @@ nothing. strongly, and would stop relying on a namespace that user code legitimately shares. 9. **Recovery tiers for impure designs.** A design escalated to `pure(false)` poisons its whole subtree for caching. Tracked-effect manifests (recording the effects a body performed, and - replaying or re-checking them on a hit) would let some of those designs cache anyway. + replaying or re-checking them on a hit) would let some of those designs cache anyway. The + [external init files](#external-init-files) record is this in miniature for one effect kind: + the file read never escalates, and its record re-checks on every hit. 10. **User documentation** of the purity model in `docs/`: the `@pure` overrides with and without named impure parameters, the "unmarked effects are the user's responsibility" contract, the static-dispatch approximation (the analysis never models subclass overrides), and the key diff --git a/internals/src/main/scala/dfhdl/internals/BitNum.scala b/internals/src/main/scala/dfhdl/internals/BitNum.scala new file mode 100644 index 000000000..de110885e --- /dev/null +++ b/internals/src/main/scala/dfhdl/internals/BitNum.scala @@ -0,0 +1,29 @@ +package dfhdl.internals + +type BitNum = 0 | 1 + +//BitNumWrapper is a wrapper for BitNum to preserve 0 or 1 values in basic operations +//The type is also used as `Bit` in the DFHDL frontend, to allow using BitNum values in DFHDL code +//and constructing DFBit DFHDL valeu types such as `Bit <> CONST`. +//TODO: implemented workaround for https://github.com/scala/scala3/issues/26550 +into sealed class BitNumWrapper(val value: Int) extends AnyVal derives CanEqual: + def unary_! : BitNumWrapper = BitNumWrapper(if value == 0 then 1 else 0) + def unary_~ : BitNumWrapper = unary_! + def |(rhs: BitNumWrapper): BitNumWrapper = + BitNumWrapper(if value == 1 || rhs.value == 1 then 1 else 0) + def &(rhs: BitNumWrapper): BitNumWrapper = + BitNumWrapper(if value == 1 && rhs.value == 1 then 1 else 0) + def ^(rhs: BitNumWrapper): BitNumWrapper = + BitNumWrapper(if value != rhs.value then 1 else 0) + def &&(rhs: BitNumWrapper): BitNumWrapper = this & rhs + def ||(rhs: BitNumWrapper): BitNumWrapper = this | rhs + def ==(rhs: BitNum): Boolean = value == rhs + def !=(rhs: BitNum): Boolean = value != rhs + +object BitNumWrapper: + def apply(value: BitNum): BitNumWrapper = new BitNumWrapper(value) + given [T <: Int & Singleton](using T <:< BitNum): Conversion[T, BitNumWrapper] = + x => BitNumWrapper(x.asInstanceOf[BitNum]) + given CanEqual[BitNumWrapper, BitNum] = CanEqual.derived + // TODO: implemented workaround for https://github.com/scala/scala3/issues/26550 + implicit def toBitNum(wrapper: BitNumWrapper): BitNum = wrapper.value.asInstanceOf[BitNum] \ No newline at end of file diff --git a/internals/src/main/scala/dfhdl/internals/DiskCache.scala b/internals/src/main/scala/dfhdl/internals/DiskCache.scala index f465c623e..49cf42872 100644 --- a/internals/src/main/scala/dfhdl/internals/DiskCache.scala +++ b/internals/src/main/scala/dfhdl/internals/DiskCache.scala @@ -46,6 +46,10 @@ class DiskCache(val cacheFolderStr: String): steps.get(name) match case null => () case step => step.onBeforeRestoreHook(value()) + override def onCacheInvalidated(name: String): Unit = + steps.get(name) match + case null => () + case step => step.onCacheInvalidatedHook() private lazy val evaluator = Evaluator(DiskStore(cacheFolderPath), listener = stepListener) @@ -89,6 +93,14 @@ class DiskCache(val cacheFolderStr: String): protected def genFiles(value: R): List[String] = Nil protected val name: String = typeName protected def cacheEnable: Boolean = true + // Hit-validation hook (Factum's `validate`): when defined, a cache hit decodes the cached + // value and passes it to the validator, and a rejected value recomputes the step as a miss, + // overwriting the entry under the same key (downstream steps re-key through the fresh + // value). Serves values that record external state the key cannot carry, e.g. init files an + // elaboration read, whose paths only the elaboration itself discovers. Leave None (the + // default) to keep Factum's lazy on-demand value decode on hits. + protected def cacheHitValidator: Option[R => Boolean] = None + protected def logCacheInvalidated(): Unit = {} // Bit-compatible with the pre-Factum implementation: the otherDeps sequence is // folded with MurmurHash3 and enters the Factum action key as a plain string. @@ -100,6 +112,7 @@ class DiskCache(val cacheFolderStr: String): private[DiskCache] def onCacheHitHook(): Unit = logCachedRun() private[DiskCache] def onBeforeRestoreHook(value: Any): Unit = cleanUpBeforeFileRestore(value.asInstanceOf[R]) + private[DiskCache] def onCacheInvalidatedHook(): Unit = logCacheInvalidated() private object stepCodec extends Codec[R]: def encode(value: R): Array[Byte] = @@ -116,17 +129,24 @@ class DiskCache(val cacheFolderStr: String): private[DiskCache] lazy val task: Task[R] = given Codec[R] = stepCodec + val validate: R => Boolean = cacheHitValidator.getOrElse(Task.alwaysValid) (prevStepOrValue: @unchecked) match case prevStep: Step[?, F] => if (hasGenFiles) - prevStep.task.cachedWithFiles(name, extraKey = otherDepsKey)(runWithFiles) - else prevStep.task.cached(name, extraKey = otherDepsKey)(run) + prevStep.task.cachedWithFiles(name, extraKey = otherDepsKey, validate = validate)( + runWithFiles + ) + else prevStep.task.cached(name, extraKey = otherDepsKey, validate = validate)(run) case prevValue: (() => F) => if (hasGenFiles) - Task.pure(()).cachedWithFiles(name, extraKey = otherDepsKey)(_ => + Task.pure(()).cachedWithFiles(name, extraKey = otherDepsKey, validate = validate)(_ => runWithFiles(prevValue()) ) - else Task.pure(()).cached(name, extraKey = otherDepsKey)(_ => run(prevValue())) + else + Task.pure(()).cached(name, extraKey = otherDepsKey, validate = validate)(_ => + run(prevValue()) + ) + end match end task // cached run, unless uncached is true and then only this step is run without caching diff --git a/internals/src/main/scala/dfhdl/internals/Exact.scala b/internals/src/main/scala/dfhdl/internals/Exact.scala index be6a997fd..23aeae0f9 100644 --- a/internals/src/main/scala/dfhdl/internals/Exact.scala +++ b/internals/src/main/scala/dfhdl/internals/Exact.scala @@ -84,9 +84,43 @@ extension [Q <: Quotes & Singleton](using quotes: Q)(term: quotes.reflect.Term) ) }.asTerm case _ => ifTerm - case t => t + // Scala widens the singleton literal types when it infers a collection's element + // type, so `Vector(0, 1, 1)` and `Vector.fill(16)(1)` both come out as + // `Vector[Int]` and lose the knowledge that every element is a bit. Such a + // collection is retyped with a `BitNum` element type, so it can be applied to a + // `Bit` vector. + case t => t.asBitCollection end match end exactTerm + + // A collection construction whose last argument list holds nothing but 0 or 1 literals + // (see the use in `exactTerm`), retyped with a `BitNum` element type. Any other term is + // returned as is. `BitNum` remains a subtype of `Int`, so a collection headed elsewhere + // than a `Bit` vector is unaffected by the narrower element type. + private def asBitCollection: quotes.reflect.Term = + import quotes.reflect.* + def allBitLiterals(args: List[Term]): Boolean = + args.nonEmpty && args.forall { + case Inlined(_, Nil, arg) => allBitLiterals(List(arg)) + case Typed(arg, _) => allBitLiterals(List(arg)) + case Repeated(elems, _) => allBitLiterals(elems) + case Literal(IntConstant(i)) => i == 0 || i == 1 + case _ => false + } + term match + case Apply(_, args) if allBitLiterals(args) => + term.tpe.widen match + // the element type is only narrowed for an `Iterable`, where it is erased to a + // reference and the retyping is therefore a no-op at runtime. An `Array`, whose + // element type survives erasure, is deliberately left alone. + case AppliedType(tycon, List(elemTpe)) + if elemTpe =:= TypeRepr.of[Int] && term.tpe <:< TypeRepr.of[Iterable[Int]] => + AppliedType(tycon, List(TypeRepr.of[BitNum])).asType match + case '[bitColl] => '{ ${ term.asExprOf[Any] }.asInstanceOf[bitColl] }.asTerm + case _ => term + case _ => term + end match + end asBitCollection end extension final class Exact[T](val value: T) extends AnyVal diff --git a/internals/src/test/scala/dfhdl/internals/DiskCacheSpec.scala b/internals/src/test/scala/dfhdl/internals/DiskCacheSpec.scala new file mode 100644 index 000000000..aeb920a37 --- /dev/null +++ b/internals/src/test/scala/dfhdl/internals/DiskCacheSpec.scala @@ -0,0 +1,52 @@ +package dfhdl.internals + +import munit.FunSuite +import java.nio.file.{Files, Path} +import java.util.concurrent.atomic.AtomicInteger + +class DiskCacheSpec extends FunSuite: + private def deleteRecursively(p: Path): Unit = + if (Files.isDirectory(p)) + val stream = Files.list(p) + try stream.forEach(deleteRecursively) + finally stream.close() + Files.deleteIfExists(p) + + // The hit-validation seam of the DFApp elaborate step: a step whose cached value records + // external state (init files an elaboration read) validates it on every hit, and a rejected + // value re-runs the step and overwrites the entry under the same key. + test("a rejected cache-hit validation re-runs the step and overwrites the entry") { + val cacheDir = Files.createTempDirectory("dfhdl-diskcache-spec") + try + val runs = AtomicInteger(0) + val invalidations = AtomicInteger(0) + var current = "v1" + var accept = true + object cache extends DiskCache(cacheDir.toString) + object step extends cache.Step[Unit, String](() => ())(): + protected def run(from: Unit): String = + runs.incrementAndGet() + current + protected def valueToCacheStr(value: String): String = value + protected def cacheStrToValue(str: String): String = str + override protected def cacheHitValidator: Option[String => Boolean] = + Some(_ => accept) + override protected def logCacheInvalidated(): Unit = + invalidations.incrementAndGet() + end step + assertEquals(step(), "v1") + assertEquals(step(), "v1") // accepted hit + assertEquals(runs.get, 1) + assertEquals(invalidations.get, 0) + accept = false + current = "v2" + assertEquals(step(), "v2") // rejected: the step re-runs like a miss + assertEquals(runs.get, 2) + assertEquals(invalidations.get, 1) + accept = true + assertEquals(step(), "v2") // the re-run overwrote the entry under the same key + assertEquals(runs.get, 2) + finally deleteRecursively(cacheDir) + end try + } +end DiskCacheSpec diff --git a/lib/src/main/scala/dfhdl/app/DFApp.scala b/lib/src/main/scala/dfhdl/app/DFApp.scala index efb4957d7..82e41d7c5 100644 --- a/lib/src/main/scala/dfhdl/app/DFApp.scala +++ b/lib/src/main/scala/dfhdl/app/DFApp.scala @@ -169,6 +169,15 @@ class DFApp: elaborated.printCodeString override protected def logCachedRun(): Unit = logger.info("Loading elaborated design from cache...") + // The elaborated DB records the init files elaboration loaded (path + contents, as + // `SourceType.InitFile` sources on the sub-DBs). The step's key cannot carry them (only + // running the elaboration discovers which files it reads), so a hit re-validates them + // against the file system instead: a changed or missing file rejects the entry, the step + // re-elaborates like a miss, and the fresh result overwrites the entry under the same key. + override protected def cacheHitValidator: Option[StagedDesign => Boolean] = + Some(_.stagedDB.initFilesUnchanged) + override protected def logCacheInvalidated(): Unit = + logger.info("An init file has changed; re-elaborating design...") protected def valueToCacheStr(value: StagedDesign): String = value.stagedDB.toJsonString protected def cacheStrToValue(str: String): StagedDesign = new StagedDesign( ir.DB.fromJsonString(str) diff --git a/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala b/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala index 0a684d045..6e93aa297 100644 --- a/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala +++ b/lib/src/main/scala/dfhdl/tools/toolsCore/Verilator.scala @@ -74,9 +74,16 @@ object Verilator extends VerilogLinter, VerilogSimulator: MemberGetSet ): String = constructCommand( "-Wall", + noDeclFileName, (!summon[LinterOptions].Werror.toBoolean).toFlag("-Wno-fatal") ) + // Verilator wants a file to be named after the one module it declares. DFHDL names a file after + // a DECLARATION and puts every design of it there (see `DB.designFileNameMap`), so a design that + // one declaration elaborated several of never matches its file name. The rule is off wholesale + // rather than waived per file: what it checks for is by construction not how DFHDL emits. + private val noDeclFileName: String = "-Wno-DECLFILENAME" + override protected def simulateCmdPreLangFlags(using CompilerOptions, SimulatorOptions, @@ -133,6 +140,7 @@ object Verilator extends VerilogLinter, VerilogSimulator: MemberGetSet ): String = constructCommand( "-Wall", + noDeclFileName, (!summon[LinterOptions].Werror.toBoolean).toFlag("-Wno-fatal") ) @@ -344,15 +352,23 @@ class VerilatorConfigPrinter(verilatorVersion: String, isToolInWindows: Boolean) case _ => None }.mkString("\n") end lintOffBlackBoxes + // The `-file` filter of the HDL file a design was emitted into. That file is named after the + // design's DECLARATION and holds every design of it (`mulByte_0`, `mulByte_1`, ... all live in + // `mulByte.sv`, see `DB.designFileNameMap`), so a waiver cannot separate one such design from + // its siblings; that is inherent to their sharing a file, and is why every filter here is + // narrowed further by a `-match` pattern on the reported name. + extension (design: DFDesignBlock) + def fileNameFilter: String = + s"${designDB.designFileNameMap.getOrElse(design, design.dclName)}.*" extension (dfVal: DFVal) def fileNameFilter: String = if (dfVal.isGlobal) s"${getSet.topName}_defs.*" - else s"${dfVal.getOwnerDesign.dclName}.*" + else dfVal.getOwnerDesign.fileNameFilter def lintOffOpenOutPorts: String = designDB.getOpenOutPorts.map: dfVal => lintOffCommand( rule = "PINCONNECTEMPTY", - file = s"${dfVal.getOwnerDesign.getOwnerDesign.dclName}.*", + file = dfVal.getOwnerDesign.getOwnerDesign.fileNameFilter, matchWild = s"*: '${dfVal.getName}'*" ) .distinct.mkString("\n") diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte.sv new file mode 100644 index 000000000..2da711da1 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte.sv @@ -0,0 +1,49 @@ +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.svh" + +module mulByte_0#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + AESByte a_lhs; + AESByte a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.svh" + +module mulByte_1#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + AESByte a_lhs; + AESByte a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.svh" + +module mulByte_2#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_0.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_0.sv deleted file mode 100644 index c8a1e0d1c..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_0.sv +++ /dev/null @@ -1,18 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.svh" - -module mulByte_0#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - AESByte a_lhs; - AESByte a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_1.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_1.sv deleted file mode 100644 index fd24596bf..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_1.sv +++ /dev/null @@ -1,18 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.svh" - -module mulByte_1#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - AESByte a_lhs; - AESByte a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_2.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_2.sv deleted file mode 100644 index 2f709135d..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.sv2009/hdl/mulByte_2.sv +++ /dev/null @@ -1,11 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.svh" - -module mulByte_2#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte.v new file mode 100644 index 000000000..541869fd7 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte.v @@ -0,0 +1,52 @@ +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_0#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_1#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_2#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_0.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_0.v deleted file mode 100644 index 6a60aa0dd..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_0.v +++ /dev/null @@ -1,19 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_0#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_1.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_1.v deleted file mode 100644 index edbd21061..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_1.v +++ /dev/null @@ -1,19 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_1#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_2.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_2.v deleted file mode 100644 index 97cd51914..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v2001/hdl/mulByte_2.v +++ /dev/null @@ -1,12 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_2#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte.v new file mode 100644 index 000000000..bbbde409e --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte.v @@ -0,0 +1,61 @@ +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_0( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_1( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "CipherNoOpaques_defs.vh" + +module mulByte_2( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "CipherNoOpaques_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_0.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_0.v deleted file mode 100644 index fde042375..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_0.v +++ /dev/null @@ -1,22 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_0( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_1.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_1.v deleted file mode 100644 index d21ef7b16..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_1.v +++ /dev/null @@ -1,22 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_1( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_2.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_2.v deleted file mode 100644 index 848b404bb..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/verilog.v95/hdl/mulByte_2.v +++ /dev/null @@ -1,15 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "CipherNoOpaques_defs.vh" - -module mulByte_2( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "CipherNoOpaques_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte.vhd new file mode 100644 index 000000000..c3b09c267 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte.vhd @@ -0,0 +1,76 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_0 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_0; + +architecture mulByte_0_arch of mulByte_0 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor a_o; +end mulByte_0_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_1 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_1; + +architecture mulByte_1_arch of mulByte_1 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor rhs xor a_o; +end mulByte_1_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_2 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_2; + +architecture mulByte_2_arch of mulByte_2 is +begin + o <= x"00" xor rhs; +end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_0.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_0.vhd deleted file mode 100644 index fc2aa822d..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_0.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_0 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_0; - -architecture mulByte_0_arch of mulByte_0 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor a_o; -end mulByte_0_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_1.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_1.vhd deleted file mode 100644 index 7376ef24a..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_1.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_1 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_1; - -architecture mulByte_1_arch of mulByte_1 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor rhs xor a_o; -end mulByte_1_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_2.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_2.vhd deleted file mode 100644 index eeb6713e5..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v2008/hdl/mulByte_2.vhd +++ /dev/null @@ -1,20 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_2 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_2; - -architecture mulByte_2_arch of mulByte_2 is -begin - o <= x"00" xor rhs; -end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte.vhd new file mode 100644 index 000000000..c3b09c267 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte.vhd @@ -0,0 +1,76 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_0 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_0; + +architecture mulByte_0_arch of mulByte_0 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor a_o; +end mulByte_0_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_1 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_1; + +architecture mulByte_1_arch of mulByte_1 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor rhs xor a_o; +end mulByte_1_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.CipherNoOpaques_pkg.all; + +entity mulByte_2 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_2; + +architecture mulByte_2_arch of mulByte_2 is +begin + o <= x"00" xor rhs; +end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_0.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_0.vhd deleted file mode 100644 index fc2aa822d..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_0.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_0 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_0; - -architecture mulByte_0_arch of mulByte_0 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor a_o; -end mulByte_0_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_1.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_1.vhd deleted file mode 100644 index 7376ef24a..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_1.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_1 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_1; - -architecture mulByte_1_arch of mulByte_1 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor rhs xor a_o; -end mulByte_1_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_2.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_2.vhd deleted file mode 100644 index eeb6713e5..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecNoOpaques/vhdl.v93/hdl/mulByte_2.vhd +++ /dev/null @@ -1,20 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.CipherNoOpaques_pkg.all; - -entity mulByte_2 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_2; - -architecture mulByte_2_arch of mulByte_2 is -begin - o <= x"00" xor rhs; -end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte.sv new file mode 100644 index 000000000..486964216 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte.sv @@ -0,0 +1,49 @@ +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.svh" + +module mulByte_0#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + AESByte a_lhs; + AESByte a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.svh" + +module mulByte_1#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + AESByte a_lhs; + AESByte a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.svh" + +module mulByte_2#(parameter logic [7:0] lhs = 8'hxx)( + input wire AESByte rhs, + output AESByte o +); + `include "dfhdl_defs.svh" + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_0.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_0.sv deleted file mode 100644 index f02dc1eeb..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_0.sv +++ /dev/null @@ -1,18 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.svh" - -module mulByte_0#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - AESByte a_lhs; - AESByte a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_1.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_1.sv deleted file mode 100644 index e7845a4c9..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_1.sv +++ /dev/null @@ -1,18 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.svh" - -module mulByte_1#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - AESByte a_lhs; - AESByte a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_2.sv b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_2.sv deleted file mode 100644 index 84b634d96..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.sv2009/hdl/mulByte_2.sv +++ /dev/null @@ -1,11 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.svh" - -module mulByte_2#(parameter logic [7:0] lhs = 8'hxx)( - input wire AESByte rhs, - output AESByte o -); - `include "dfhdl_defs.svh" - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte.v new file mode 100644 index 000000000..df933e474 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte.v @@ -0,0 +1,52 @@ +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_0#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_1#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_2#(parameter [7:0] lhs = 8'hxx)( + input wire [7:0] rhs, + output wire [7:0] o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_0.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_0.v deleted file mode 100644 index a09a43bfc..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_0.v +++ /dev/null @@ -1,19 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_0#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_1.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_1.v deleted file mode 100644 index 88c449693..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_1.v +++ /dev/null @@ -1,19 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_1#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_2.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_2.v deleted file mode 100644 index 454d9f085..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v2001/hdl/mulByte_2.v +++ /dev/null @@ -1,12 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_2#(parameter [7:0] lhs = 8'hxx)( - input wire [7:0] rhs, - output wire [7:0] o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte.v new file mode 100644 index 000000000..5fc508e59 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte.v @@ -0,0 +1,61 @@ +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_0( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_1( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + wire [7:0] a_lhs; + wire [7:0] a_o; + xtime a( + .lhs /*<--*/ (a_lhs), + .o /*-->*/ (a_o) + ); + assign a_lhs = rhs; + assign o = 8'h00 ^ rhs ^ a_o; +endmodule + +`default_nettype none +`timescale 1ns/1ps +`include "Cipher_defs.vh" + +module mulByte_2( + rhs, + o +); + `include "dfhdl_defs.vh" + `include "Cipher_defs.vh" + parameter [7:0] lhs = 8'hxx; + input wire [7:0] rhs; + output wire [7:0] o; + assign o = 8'h00 ^ rhs; +endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_0.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_0.v deleted file mode 100644 index a3574b22d..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_0.v +++ /dev/null @@ -1,22 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_0( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_1.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_1.v deleted file mode 100644 index 3156cab3f..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_1.v +++ /dev/null @@ -1,22 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_1( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - wire [7:0] a_lhs; - wire [7:0] a_o; - xtime a( - .lhs /*<--*/ (a_lhs), - .o /*-->*/ (a_o) - ); - assign a_lhs = rhs; - assign o = 8'h00 ^ rhs ^ a_o; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_2.v b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_2.v deleted file mode 100644 index 697d953d9..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/verilog.v95/hdl/mulByte_2.v +++ /dev/null @@ -1,15 +0,0 @@ -`default_nettype none -`timescale 1ns/1ps -`include "Cipher_defs.vh" - -module mulByte_2( - rhs, - o -); - `include "dfhdl_defs.vh" - `include "Cipher_defs.vh" - parameter [7:0] lhs = 8'hxx; - input wire [7:0] rhs; - output wire [7:0] o; - assign o = 8'h00 ^ rhs; -endmodule diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte.vhd new file mode 100644 index 000000000..b7ba673c6 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte.vhd @@ -0,0 +1,76 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_0 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_0; + +architecture mulByte_0_arch of mulByte_0 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor a_o; +end mulByte_0_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_1 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_1; + +architecture mulByte_1_arch of mulByte_1 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor rhs xor a_o; +end mulByte_1_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_2 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_2; + +architecture mulByte_2_arch of mulByte_2 is +begin + o <= x"00" xor rhs; +end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_0.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_0.vhd deleted file mode 100644 index 6662783ac..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_0.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_0 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_0; - -architecture mulByte_0_arch of mulByte_0 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor a_o; -end mulByte_0_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_1.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_1.vhd deleted file mode 100644 index f6567b48c..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_1.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_1 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_1; - -architecture mulByte_1_arch of mulByte_1 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor rhs xor a_o; -end mulByte_1_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_2.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_2.vhd deleted file mode 100644 index fbd334417..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v2008/hdl/mulByte_2.vhd +++ /dev/null @@ -1,20 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_2 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_2; - -architecture mulByte_2_arch of mulByte_2 is -begin - o <= x"00" xor rhs; -end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte.vhd new file mode 100644 index 000000000..b7ba673c6 --- /dev/null +++ b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte.vhd @@ -0,0 +1,76 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_0 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_0; + +architecture mulByte_0_arch of mulByte_0 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor a_o; +end mulByte_0_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_1 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_1; + +architecture mulByte_1_arch of mulByte_1 is + signal a_lhs : AESByte; + signal a_o : AESByte; +begin + a : entity work.xtime(xtime_arch) port map ( + lhs => a_lhs, + o => a_o + ); + a_lhs <= rhs; + o <= x"00" xor rhs xor a_o; +end mulByte_1_arch; + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.dfhdl_pkg.all; +use work.Cipher_pkg.all; + +entity mulByte_2 is +generic ( + lhs : std_logic_vector(7 downto 0) +); +port ( + rhs : in AESByte; + o : out AESByte +); +end mulByte_2; + +architecture mulByte_2_arch of mulByte_2 is +begin + o <= x"00" xor rhs; +end mulByte_2_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_0.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_0.vhd deleted file mode 100644 index 6662783ac..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_0.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_0 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_0; - -architecture mulByte_0_arch of mulByte_0 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor a_o; -end mulByte_0_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_1.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_1.vhd deleted file mode 100644 index f6567b48c..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_1.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_1 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_1; - -architecture mulByte_1_arch of mulByte_1 is - signal a_lhs : AESByte; - signal a_o : AESByte; -begin - a : entity work.xtime(xtime_arch) port map ( - lhs => a_lhs, - o => a_o - ); - a_lhs <= rhs; - o <= x"00" xor rhs xor a_o; -end mulByte_1_arch; diff --git a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_2.vhd b/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_2.vhd deleted file mode 100644 index fbd334417..000000000 --- a/lib/src/test/resources/ref/dfhdl.AES.CipherSpecWithOpaques/vhdl.v93/hdl/mulByte_2.vhd +++ /dev/null @@ -1,20 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use work.dfhdl_pkg.all; -use work.Cipher_pkg.all; - -entity mulByte_2 is -generic ( - lhs : std_logic_vector(7 downto 0) -); -port ( - rhs : in AESByte; - o : out AESByte -); -end mulByte_2; - -architecture mulByte_2_arch of mulByte_2 is -begin - o <= x"00" xor rhs; -end mulByte_2_arch; diff --git a/plugin/src/main/scala/plugin/MetaContextGenPhase.scala b/plugin/src/main/scala/plugin/MetaContextGenPhase.scala index 885fd03d2..bde30fe71 100755 --- a/plugin/src/main/scala/plugin/MetaContextGenPhase.scala +++ b/plugin/src/main/scala/plugin/MetaContextGenPhase.scala @@ -121,6 +121,17 @@ class MetaContextGenPhase(setting: Setting) extends CommonPhase: extension (sym: Symbol) def fixedFullName(using Context): String = sym.fullName.toString.replace("._$", ".") + // A compiler-generated function (a proxy, an anonymous function, a default getter, ...) + // carries a `$` in its name and must not mint a new anonymous context: the value it builds + // belongs to whatever context was propagated into it. + // + // An inline accessor (`inline$foo`) is the exception. The compiler introduces it only so an + // inline expansion can reach a member it cannot name directly, so it stands for the + // user-written call and gets an anonymous context like any other. Without this, an + // interpolator such as `b"4'1001"` (expanded to `StrInterp.inline$interpolate`) would keep + // the enclosing design's context and be printed under the design instance's own name. + def keepsPropagatedContext(using Context): Boolean = + sym.name.exclude(NameKinds.InlineAccessorName).toString.contains("$") private def ignoreValDef(tree: ValDef)(using Context): Boolean = tree.name.toString match case inlinedName(prefix) => @@ -223,7 +234,7 @@ class MetaContextGenPhase(setting: Setting) extends CommonPhase: // at all and just keep the propagated context. case None => // keeping the propagated context - if (fixedApply.fun.symbol.name.toString.contains("$")) fixedApply + if (fixedApply.fun.symbol.keepsPropagatedContext) fixedApply // generating a new anonymous context else // An apply inside a library inline expansion (or synthesized diff --git a/project/build.properties b/project/build.properties index 5f6d607d4..d8f4c25ee 100755 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.12.15 \ No newline at end of file +sbt.version = 1.13.0 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 1bc1a1dcf..5faca1970 100755 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ logLevel := Level.Warn -addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.12.0") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.12.1")