From 81a09db2f80a34e5e16a91e610ad861bafad2834 Mon Sep 17 00:00:00 2001 From: oronpo Date: Thu, 15 Aug 2024 15:33:13 +0300 Subject: [PATCH] add support for Verilog Bin/Hex standard file initialization --- .../main/scala/dfhdl/compiler/ir/DFType.scala | 50 ++++ .../dfhdl/compiler/ir/InitFileFormat.scala | 228 +++++++++++++++++- .../StagesSpec/PrintCodeStringSpec.scala | 4 +- core/src/main/scala/dfhdl/core/DFBits.scala | 57 +---- core/src/main/scala/dfhdl/core/DFVal.scala | 36 ++- core/src/test/resources/bits8x4.bin | 4 + core/src/test/resources/bits8x4.empty | 0 core/src/test/resources/bits8x4.hex | 4 + .../test/scala/CoreSpec/DFVectorSpec.scala | 43 +++- 9 files changed, 361 insertions(+), 65 deletions(-) create mode 100644 core/src/test/resources/bits8x4.bin create mode 100644 core/src/test/resources/bits8x4.empty create mode 100644 core/src/test/resources/bits8x4.hex diff --git a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala index 0102af928..2f6b26761 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/DFType.scala @@ -4,6 +4,7 @@ import dfhdl.internals.* import scala.collection.immutable.{ListMap, ListSet} import scala.reflect.ClassTag +import scala.util.boundary, boundary.break sealed trait DFType extends Product, Serializable, HasRefCompare[DFType] derives CanEqual: type Data @@ -112,6 +113,55 @@ final case class DFBits(widthParamRef: IntParamRef) extends DFType: object DFBits extends DFType.Companion[DFBits, (BitVector, BitVector)]: def apply(width: Int): DFBits = DFBits(IntParamRef(width)) + def dataFromBinString( + bin: String + ): Either[String, (BitVector, BitVector)] = boundary { + val (valueBits, bubbleBits) = + bin.foldLeft((BitVector.empty, BitVector.empty)) { + case (t, '_' | ' ') => t // ignoring underscore or space + case ((v, b), c) => + c match // bin mode + case '?' => (v :+ false, b :+ true) + case '0' => (v :+ false, b :+ false) + case '1' => (v :+ true, b :+ false) + case x => + break(Left(s"Found invalid binary character: $x")) + } + Right((valueBits, bubbleBits)) + } + private val isHex = "[0-9a-fA-F]".r + def dataFromHexString( + hex: String + ): Either[String, (BitVector, BitVector)] = boundary { + val (valueBits, bubbleBits, binMode) = + hex.foldLeft((BitVector.empty, BitVector.empty, false)) { + case (t, '_' | ' ') => t // ignoring underscore or space + case ((v, b, false), c) => + c match // hex mode + case '{' => (v, b, true) + case '?' => (v ++ BitVector.low(4), b ++ BitVector.high(4), false) + case isHex() => + ( + v ++ BitVector.fromHex(c.toString).get, + b ++ BitVector.low(4), + false + ) + case x => + break(Left(s"Found invalid hex character: $x")) + case ((v, b, true), c) => + c match // bin mode + case '}' => (v, b, false) + case '?' => (v :+ false, b :+ true, true) + case '0' => (v :+ false, b :+ false, true) + case '1' => (v :+ true, b :+ false, true) + case x => + break(Left(s"Found invalid binary character in binary mode: $x")) + } + if (binMode) Left(s"Missing closing braces of binary mode") + else Right((valueBits, bubbleBits)) + } +end DFBits + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// 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 df9ab1ad2..384005a7e 100644 --- a/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala +++ b/compiler/ir/src/main/scala/dfhdl/compiler/ir/InitFileFormat.scala @@ -1,4 +1,230 @@ package dfhdl.compiler.ir +import scala.io.Source +import scala.util.matching.Regex +import java.io.FileNotFoundException +import scala.util.control.Exception._ +import dfhdl.internals.CommonOps.bitsWidth +import dfhdl.internals.* enum InitFileFormat derives CanEqual: - case Auto, VerilogBin, VerilogHex, AMDXilinxCOE, AMDXilinxMEM, IntelAlteraMIF, IntelAlteraHEX + case Auto, VerilogBin, VerilogHex + // AMDXilinxCOE, IntelAlteraMIF, IntelAlteraHEX + // LatticeMEM, AMDXilinxMEM + +object InitFileFormat: + import InitFileFormat.* + def readInitFile( + fileName: String, + fileFormat: InitFileFormat, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + val source = + try Source.fromResource(fileName) + catch + case _: FileNotFoundException => + try Source.fromFile(fileName) + catch + case _: FileNotFoundException => + 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." + ) + + val fileContents = source.getLines().mkString("\n") + val detectedFormat = fileFormat match + case Auto => detectAutoFormat(fileName, fileContents, dataWidth) + case _ => fileFormat + try + (detectedFormat: @unchecked) match + case VerilogBin => readVerilogBin(fileContents, arrLen, dataWidth) + case VerilogHex => readVerilogHex(fileContents, arrLen, dataWidth) + // case AMDXilinxCOE => readAMDXilinxCOE(fileContents, arrLen, dataWidth) + // case IntelAlteraMIF => readIntelAlteraMIF(fileContents, arrLen, dataWidth) + // case IntelAlteraHEX => readIntelAlteraHEX(fileContents, arrLen, dataWidth) + // case AMDXilinxMEM => readAMDXilinxMEM(fileContents, arrLen, dataWidth) + // case LatticeMEM => readLatticeMEM(fileContents, arrLen, dataWidth) + catch + case e: DataError => + import e.* + throw new IllegalArgumentException( + s"Init file error detected in $detectedFormat formatted ${fileName}:$lineNum\n$msg" + ) + end try + end readInitFile + + private val verilogCommentPattern = """//.*|/\*.*?\*/""".r + private val validBinPattern = + "[01]+".r // currently not supporting [xXzZ_] characters for simplification + private val validHexPattern = + "[0-9a-fA-F]+".r // currently not supporting [xXzZ_] characters for simplification + class DataError(val msg: String, val lineNum: Int) extends IllegalArgumentException(msg) + extension (content: String) + private def contentCleanup( + singleLineCommentPattern: String = "", + multiLineCommentPattern: String = "" + ): String = + val replaceWithNewlines = (comment: String) => + val lineCount = comment.count(_ == '\n') // Count the number of newlines in the comment + "\n" * lineCount // Replace with the same number of newline characters + // first, remove multiline comments + val noMultiLineComments = + if (multiLineCommentPattern.isEmpty) content + else multiLineCommentPattern.r.replaceAllIn(content, m => replaceWithNewlines(m.matched)) + // second, remove singleline comments + val noSingleLineComment = + if (singleLineCommentPattern.isEmpty) noMultiLineComments + else singleLineCommentPattern.r.replaceAllIn(noMultiLineComments, " ") + // third, cleanup removing multi-spaces + val noMultiSpaces = noSingleLineComment.replaceAll("""[ \t]+""", " ").trim + // finally, trim all lines + noMultiSpaces.linesIterator.map(_.trim).mkString("\n") + end contentCleanup + private def verilogCleanup: String = contentCleanup( + singleLineCommentPattern = """//.*(\n|\r|\r\n)""", + multiLineCommentPattern = """/\*[\s\S]*?\*/""" + ) + end extension + + extension (word: String) + private def isDataBin: Boolean = validBinPattern.matches(word) + private def isDataHex: Boolean = validHexPattern.matches(word) + + private def detectAutoFormat( + fileName: String, + fileContents: String, + dataWidth: Int + ): InitFileFormat = + val suffix = fileName.split("\\.").last.toLowerCase() + suffix match + // case "coe" => AMDXilinxCOE + // case "mif" => IntelAlteraMIF + // case "mem" => + // if ("""\#Format\s+=""".r.matches(fileContents)) LatticeMEM + // else AMDXilinxMEM + // case "hex" => + // if (fileContents.startsWith(":")) IntelAlteraHEX + // else VerilogHex + case _ => + val firstData = + fileContents.verilogCleanup.linesIterator + .filter(line => !line.startsWith("@") && line.nonEmpty) + .nextOption().getOrElse("").split(" ").headOption.getOrElse("") + if (firstData.isDataBin && firstData.length() == dataWidth) VerilogBin + else if (firstData.isDataHex) VerilogHex + else + throw new IllegalArgumentException( + s"Could not automatically detect the init file format of $fileName" + ) + end match + + end detectAutoFormat + + private def readVerilogStdFile( + contents: String, + arrLen: Int, + dataWidth: Int, + isBinary: Boolean + ): Vector[(BitVector, BitVector)] = + // array initialization with everything as invalid + val bubbleCell = (BitVector.low(dataWidth), BitVector.high(dataWidth)) + val result = Array.fill(arrLen)(bubbleCell) + // starting at address zero + // in the verilog standard the address refers to the array index + var currentAddress = 0 + contents.verilogCleanup.linesIterator.zipWithIndex.forall { + case (line, lineNum) if currentAddress < arrLen => + if (line.nonEmpty) + line.split(" ").foreach { word => + def invalidDataCharacterError() = + throw new DataError(s"Invalid data character detected: $word", lineNum) + def invalidDataWidthError(wordWidth: Int) = throw new DataError( + s"Invalid data width detected (expected $dataWidth but found $wordWidth): $word", + lineNum + ) + // address + if (word.startsWith("@")) + val addressStr = word.drop(1) + catching(classOf[NumberFormatException]).opt(Integer.parseInt(addressStr, 16)) match + case Some(addr) if addr < arrLen => currentAddress = addr + case _ => throw new DataError(s"Invalid address specification: $word", lineNum) + // binary data + else if (isBinary) + if (!word.isDataBin) invalidDataCharacterError() + val dfhdlWord = word.replaceAll("[zZxX]", "?") + val wordData = DFBits.dataFromBinString(dfhdlWord).toOption.get + val wordWidth = wordData._1.lengthOfValue.toInt + // for binary we require exact character width + if (dfhdlWord.length != dataWidth) invalidDataWidthError(wordWidth) + result(currentAddress) = wordData + currentAddress += 1 + // hexadecimal data + else + if (!word.isDataHex) invalidDataCharacterError() + val dfhdlWord = word.replaceAll("[zZxX]", "?") + val wordData = DFBits.dataFromHexString(dfhdlWord).toOption.get + val wordWidth = wordData._1.lengthOfValue.toInt + // hex words can be resized if they are smaller than the expected data width + if (wordWidth > dataWidth) invalidDataWidthError(wordWidth) + result(currentAddress) = + (wordData._1.resize(dataWidth), wordData._2.resize(dataWidth)) + currentAddress += 1 + end if + } + end if + true + case _ => false // already reached the end of the array, so stopping loop + } + result.toVector + end readVerilogStdFile + + /* https://docs.amd.com/r/2023.1-English/ug901-vivado-synthesis/Initializing-RAM-Contents */ + /* https://docs.amd.com/r/2023.1-English/ug901-vivado-synthesis/Specifying-RAM-Initial-Contents-in-an-External-Data-File */ + /* https://docs.amd.com/r/2023.1-English/ug901-vivado-synthesis/Initializing-Block-RAM-From-an-External-Data-File-Verilog */ + private def readVerilogBin( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + readVerilogStdFile(contents, arrLen, dataWidth, isBinary = true) + private def readVerilogHex( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + readVerilogStdFile(contents, arrLen, dataWidth, isBinary = false) + /* https://docs.amd.com/r/en-US/ug896-vivado-ip/COE-File-Syntax */ + private def readAMDXilinxCOE( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + ??? + /* https://docs.amd.com/r/en-US/ug1580-updatemem/Memory-Files */ + private def readAMDXilinxMEM( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + ??? + /* https://www.intel.com/content/www/us/en/programmable/quartushelp/current/index.htm#reference/glossary/def_mif.htm */ + private def readIntelAlteraMIF( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + ??? + /* https://www.intel.com/content/www/us/en/programmable/quartushelp/current/index.htm#reference/glossary/def_hexfile.htm */ + private def readIntelAlteraHEX( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + ??? + /* https://www.gsp.com/cgi-bin/man.cgi?topic=srec_mem */ + private def readLatticeMEM( + contents: String, + arrLen: Int, + dataWidth: Int + ): Vector[(BitVector, BitVector)] = + ??? +end InitFileFormat diff --git a/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala b/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala index 790dc9abd..c4bd75ba3 100644 --- a/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala +++ b/compiler/stages/src/test/scala/StagesSpec/PrintCodeStringSpec.scala @@ -717,7 +717,7 @@ class PrintCodeStringSpec extends StageSpec: val DATA_WIDTH: Int <> CONST = 4, val ADDR_WIDTH: Int <> CONST = 4 ) extends EDDesign: - val ram = Bits(DATA_WIDTH) X (2 ** ADDR_WIDTH) <> VAR.SHARED initFile "test" + val ram = Bits(DATA_WIDTH) X (2 ** ADDR_WIDTH) <> VAR.SHARED val a, b = new EDDomain: val clk = Bit <> IN @@ -739,7 +739,7 @@ class PrintCodeStringSpec extends StageSpec: | val DATA_WIDTH: Int <> CONST = 4, | val ADDR_WIDTH: Int <> CONST = 4 |) extends EDDesign: - | val ram = Bits(DATA_WIDTH) X (2 ** ADDR_WIDTH) <> VAR.SHARED initFile "test" + | val ram = Bits(DATA_WIDTH) X (2 ** ADDR_WIDTH) <> VAR.SHARED | val a = new EDDomain: | val clk = Bit <> IN | val data = Bits(DATA_WIDTH) <> IN diff --git a/core/src/main/scala/dfhdl/core/DFBits.scala b/core/src/main/scala/dfhdl/core/DFBits.scala index 7542d3048..fce605481 100644 --- a/core/src/main/scala/dfhdl/core/DFBits.scala +++ b/core/src/main/scala/dfhdl/core/DFBits.scala @@ -104,62 +104,15 @@ object DFBits: object StrInterp: private[DFBits] val widthExp = "([0-9]+)'(.*)".r - def fromBinString( - bin: String - ): Either[String, (BitVector, BitVector)] = boundary { - val (valueBits, bubbleBits) = - bin.foldLeft((BitVector.empty, BitVector.empty)) { - case (t, '_' | ' ') => t // ignoring underscore or space - case ((v, b), c) => - c match // bin mode - case '?' => (v :+ false, b :+ true) - case '0' => (v :+ false, b :+ false) - case '1' => (v :+ true, b :+ false) - case x => - break(Left(s"Found invalid binary character: $x")) - } - Right((valueBits, bubbleBits)) - } private[DFBits] val isHex = "[0-9a-fA-F]".r - def fromHexString( - hex: String - ): Either[String, (BitVector, BitVector)] = boundary { - val (valueBits, bubbleBits, binMode) = - hex.foldLeft((BitVector.empty, BitVector.empty, false)) { - case (t, '_' | ' ') => t // ignoring underscore or space - case ((v, b, false), c) => - c match // hex mode - case '{' => (v, b, true) - case '?' => (v ++ BitVector.low(4), b ++ BitVector.high(4), false) - case isHex() => - ( - v ++ BitVector.fromHex(c.toString).get, - b ++ BitVector.low(4), - false - ) - case x => - break(Left(s"Found invalid hex character: $x")) - case ((v, b, true), c) => - c match // bin mode - case '}' => (v, b, false) - case '?' => (v :+ false, b :+ true, true) - case '0' => (v :+ false, b :+ false, true) - case '1' => (v :+ true, b :+ false, true) - case x => - break(Left(s"Found invalid binary character in binary mode: $x")) - } - if (binMode) Left(s"Missing closing braces of binary mode") - else Right((valueBits, bubbleBits)) - } - extension (fullTerm: String) private[DFBits] def interpolate[W <: IntP]( op: String, explicitWidthOption: Option[IntP] )(using DFC): DFConstOf[DFBits[W]] = val fromString = op match - case "b" => fromBinString(fullTerm) - case "h" => fromHexString(fullTerm) + case "b" => ir.DFBits.dataFromBinString(fullTerm) + case "h" => ir.DFBits.dataFromHexString(fullTerm) var (valueBits, bubbleBits) = fromString.toOption.get explicitWidthOption.foreach(ew => val updatedWidth = IntParam.forced(ew).toScalaInt @@ -182,8 +135,8 @@ object DFBits: case Literal(StringConstant(t)) => val opStr = opExpr.value.get val res = opStr match - case "b" => fromBinString(t) - case "h" => fromHexString(t) + case "b" => ir.DFBits.dataFromBinString(t) + case "h" => ir.DFBits.dataFromHexString(t) res match case Right((valueBits, bubbleBits)) => explicitWidthTpeOption match @@ -213,7 +166,7 @@ object DFBits: // Unclear why, but the compiler crashes if we do not separate these definitions from StrInterp object StrInterpOps: - import StrInterp.{fromBinString, fromHexString, interpolate, isHex, widthExp} + import StrInterp.{interpolate, isHex, widthExp} opaque type BinStrCtx <: StringContext = StringContext object BinStrCtx: extension (inline sc: BinStrCtx) diff --git a/core/src/main/scala/dfhdl/core/DFVal.scala b/core/src/main/scala/dfhdl/core/DFVal.scala index 8f46fb9e5..8e9fa38fe 100644 --- a/core/src/main/scala/dfhdl/core/DFVal.scala +++ b/core/src/main/scala/dfhdl/core/DFVal.scala @@ -556,8 +556,8 @@ object DFVal extends DFValLP: else dfVal.initForced(Nil) } - extension [W <: IntP, D <: NonEmptyTuple, A, C, I, P]( - dfVal: DFVal[DFVector[DFBits[W], D], Modifier[A, C, I, P]] + extension [W <: IntP, D1 <: IntP, A, C, I, P]( + dfVal: DFVal[DFVector[DFBits[W], Tuple1[D1]], Modifier[A, C, I, P]] ) infix def initFile( path: String, @@ -565,12 +565,32 @@ object DFVal extends DFValLP: )(using DFC, InitCheck[I] - ): DFVal[DFVector[DFBits[W], D], Modifier[A, C, Modifier.Initialized, P]] = - val initFileFunc = - DFVal.Func(dfVal.dfType, DFVal.Func.Op.InitFile(format, path), List.empty[ir.DFVal])(using - dfc.anonymize - ).asConstOf[DFVector[DFBits[W], D]] - dfVal.initForced(List(initFileFunc)) + ): DFVal[DFVector[DFBits[W], Tuple1[D1]], Modifier[A, C, Modifier.Initialized, P]] = trydf: + val vectorType = dfVal.dfType + import DFVector.{lengthInt, cellType} + val data = ir.InitFileFormat.readInitFile( + path, + format, + vectorType.lengthInt, + vectorType.cellType.widthInt + ) + val initFileConst = DFVal.Const(vectorType, data) + dfVal.initForced(List(initFileConst)) + // TODO: for now, we read the data immediately. In the future, incremental compilation will make + // it beneficial to wait for the backend last stages to do so. + // infix def initFile( + // path: String, + // format: ir.InitFileFormat = ir.InitFileFormat.Auto + // )(using + // DFC, + // InitCheck[I] + // ): DFVal[DFVector[DFBits[W], D], Modifier[A, C, Modifier.Initialized, P]] = + // val initFileFunc = + // DFVal.Func(dfVal.dfType, DFVal.Func.Op.InitFile(format, path), List.empty[ir.DFVal])(using + // dfc.anonymize + // ).asConstOf[DFVector[DFBits[W], D]] + // dfVal.initForced(List(initFileFunc)) + end extension implicit def BooleanHack(from: DFValOf[DFBoolOrBit])(using DFC): Boolean = ??? diff --git a/core/src/test/resources/bits8x4.bin b/core/src/test/resources/bits8x4.bin new file mode 100644 index 000000000..62349339c --- /dev/null +++ b/core/src/test/resources/bits8x4.bin @@ -0,0 +1,4 @@ +00011000 +00100100 +01000010 +10000001 \ No newline at end of file diff --git a/core/src/test/resources/bits8x4.empty b/core/src/test/resources/bits8x4.empty new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/test/resources/bits8x4.hex b/core/src/test/resources/bits8x4.hex new file mode 100644 index 000000000..873363e7d --- /dev/null +++ b/core/src/test/resources/bits8x4.hex @@ -0,0 +1,4 @@ +18 +24 +42 +81 \ No newline at end of file diff --git a/core/src/test/scala/CoreSpec/DFVectorSpec.scala b/core/src/test/scala/CoreSpec/DFVectorSpec.scala index c7cc76ddf..a4870c4e8 100644 --- a/core/src/test/scala/CoreSpec/DFVectorSpec.scala +++ b/core/src/test/scala/CoreSpec/DFVectorSpec.scala @@ -117,13 +117,52 @@ class DFVectorSpec extends DFSpec: val v7: UInt[4] X zeroP.type X 5 <> CONST = all(all(0)) } val w: Int <> CONST = 4 - val v8: (Bits[w.type] X len.type) <> CONST = all(all(0)) + val v8: Bits[w.type] X len.type <> CONST = all(all(0)) } } test("Big Endian Packed Order") { - val v: (Bits[8] X 4) <> CONST = Vector(h"12", h"34", h"56", h"78") + val v: Bits[8] X 4 <> CONST = Vector(h"12", h"34", h"56", h"78") val b = h"12345678" assert((b == v.bits).toScalaBoolean) assert((b.as(Bits[8] X 4) == v).toScalaBoolean) } + // TODO: missing address and don't care value check + test("Vector file initialization") { + assertCodeString( + """|val v1 = Bits(8) X 4 <> VAR init DFVector(Bits(8) X 4)(h"18", h"24", h"42", h"81") + |val v2 = Bits(8) X 4 <> VAR init DFVector(Bits(8) X 4)(h"??", h"??", h"??", h"??") + |val v3 = Bits(8) X 4 <> VAR init DFVector(Bits(8) X 4)(h"18", h"24", h"42", h"81") + |""".stripMargin + ) { + val v1 = Bits(8) X 4 <> VAR initFile "bits8x4.bin" + val v2 = Bits(8) X 4 <> VAR initFile ("bits8x4.empty", InitFileFormat.VerilogBin) + val v3 = Bits(8) X 4 <> VAR initFile "bits8x4.hex" + assertRuntimeErrorLog( + """|Init file not found: bits8x4.nofile + |make sure either to place the file in your Scala project resource folder or provide a proper relative/absolute path. + |""".stripMargin + ) { + val v1 = Bits(8) X 4 <> VAR initFile "bits8x4.nofile" + } + assertRuntimeErrorLog( + "Could not automatically detect the init file format of bits8x4.empty" + ) { + val v1 = Bits(8) X 4 <> VAR initFile "bits8x4.empty" + } + assertRuntimeErrorLog( + """|Init file error detected in VerilogHex formatted bits8x4.bin:0 + |Invalid data width detected (expected 8 but found 17): 00011000 + |""".stripMargin + ) { + val v1 = Bits(8) X 4 <> VAR initFile ("bits8x4.bin", InitFileFormat.VerilogHex) + } + assertRuntimeErrorLog( + """|Init file error detected in VerilogBin formatted bits8x4.hex:0 + |Invalid data character detected: 18 + |""".stripMargin + ) { + val v1 = Bits(8) X 4 <> VAR initFile ("bits8x4.hex", InitFileFormat.VerilogBin) + } + } + } end DFVectorSpec