Skip to content

Commit

Permalink
Merge pull request #138 from DFiantHDL/vector_fix
Browse files Browse the repository at this point in the history
Fix #128 - support vectors (arrays) under VHDL 2008 dialect
  • Loading branch information
soronpo authored May 14, 2024
2 parents 1ce8756 + 2aa1178 commit b75879b
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ protected trait VHDLOwnerPrinter extends AbstractOwnerPrinter:
end csEntityDcl
def archName(design: DFDesignBlock): String = s"${design.dclName}_arch"
def csArchitectureDcl(design: DFDesignBlock): String =
val localTypeDcls = printer.csLocalTypeDcls(design)
val localTypeDcls = printer.csLocalTypeDcls(design).emptyOr(x => s"$x\n")
val vectorTypeDcls =
printer.getLocalVectorTypes(design).view.map(printer.csDFVectorDclsLocal)
.mkString("\n").emptyOr(x => s"$x\n")
val structConvFuncs =
getSet.designDB.getLocalNamedDFTypes(design).view
.collect { case dfType: DFStruct => printer.csDFStructConvFuncsBody(dfType) }
.mkString("\n").emptyOr(x => s"$x\n")
val designMembers = design.members(MemberView.Folded)
val dfValDcls =
designMembers.view
Expand All @@ -63,7 +70,7 @@ protected trait VHDLOwnerPrinter extends AbstractOwnerPrinter:
.toList
.emptyOr(_.mkString("\n"))
val declarations =
s"${localTypeDcls.emptyOr(x => s"$x\n")}$dfValDcls".emptyOr(v => s"\n${v.hindent}")
s"$localTypeDcls$vectorTypeDcls$structConvFuncs$dfValDcls".emptyOr(v => s"\n${v.hindent}")
val statements = csDFMembers(designMembers.filter {
case _: DFVal.Dcl => false
case DclConst() => false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt
def globalFileName: String = s"${printer.packageName}.vhd"
def designFileName(designName: String): String = s"$designName.vhd"
override def csGlobalFileContent: String =
val vectorTypeDcls =
printer.globalVectorTypes.view.map(printer.csDFVectorDclsGlobal)
.mkString("\n").emptyOr(x => s"$x\n")
val structConvFuncsDcl =
getSet.designDB.getGlobalNamedDFTypes.view
.collect { case dfType: DFStruct => printer.csDFStructConvFuncsDcl(dfType) }
Expand All @@ -57,7 +60,7 @@ class VHDLPrinter(using val getSet: MemberGetSet, val printerOptions: PrinterOpt
|use ieee.numeric_std.all;
|
|package ${printer.packageName} is
|${super.csGlobalFileContent + structConvFuncsDcl}
|${super.csGlobalFileContent + vectorTypeDcls + structConvFuncsDcl}
|function cadd(A, B : unsigned) return unsigned;
|function cadd(A, B : signed) return signed;
|function csub(A, B : unsigned) return unsigned;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ import dfhdl.internals.*

import scala.collection.mutable
import scala.collection.immutable.{ListSet, ListMap}
import scala.annotation.tailrec
protected trait VHDLTypePrinter extends AbstractTypePrinter:
type TPrinter <: VHDLPrinter
def csDFBoolOrBit(dfType: DFBoolOrBit, typeCS: Boolean): String = dfType match
case DFBool => "boolean"
case DFBit => "std_logic"
def csDFBoolOrBit(dfType: DFBoolOrBit, typeCS: Boolean): String =
dfType match
case DFBool => "boolean"
case DFBit => "std_logic"
def csDFBits(dfType: DFBits, typeCS: Boolean): String =
s"std_logic_vector(${dfType.widthParamRef.uboundCS} downto 0)"
if (typeCS) "std_logic_vector"
else s"std_logic_vector(${dfType.widthParamRef.uboundCS} downto 0)"
def csDFDecimal(dfType: DFDecimal, typeCS: Boolean): String =
import dfType.*
(signed, fractionWidth) match
case (false, 0) => s"unsigned(${widthParamRef.uboundCS} downto 0)"
case (false, 0) =>
if (typeCS) "unsigned"
else s"unsigned(${widthParamRef.uboundCS} downto 0)"
case (true, 0) =>
if (dfType.isDFInt32) "integer"
else if (typeCS) "signed"
else s"signed(${widthParamRef.uboundCS} downto 0)"
case (false, _) => ???
case (true, _) => ???
Expand All @@ -33,56 +39,83 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter:
.hindent
s"type ${csDFEnumTypeName(dfType)} is (\n$entries\n);"
def csDFEnum(dfType: DFEnum, typeCS: Boolean): String = csDFEnumTypeName(dfType)
private lazy val vectorCellTypes: ListMap[DFType, Option[DFDesignBlock]] =
def flatten(dfType: DFType): ListSet[DFType] =
def getVectorTypes(dcls: Iterable[DFVal]): ListMap[String, Int] =
def flatten(dfType: DFType): List[DFVector] =
dfType match
case dt: DFStruct =>
ListSet.from(dt.fieldMap.values.flatMap(flatten))
dt.fieldMap.values.flatMap(flatten).toList
case dt: DFOpaque =>
flatten(dt.actualType)
case dt: DFVector =>
flatten(dt.cellType) + dt.cellType
case _ => ListSet.empty
getSet.designDB.members.foldLeft(ListMap.empty[DFType, Option[DFDesignBlock]]) {
case (vectorCellTypeMap, dfVal: DFVal) =>
val dfTypes = flatten(dfVal.dfType)
if (dfTypes.isEmpty) vectorCellTypeMap
else if (dfVal.isPort)
vectorCellTypeMap ++ dfTypes.map(t => (t -> None)) // IO means a global vector type
else
dfTypes.foldLeft(vectorCellTypeMap) { case (vectorCellTypeMap, dfType) =>
vectorCellTypeMap.get(dfType) match
case Some(Some(owner)) => // named type already found
if (owner == dfVal.getOwnerDesign)
vectorCellTypeMap // same design block -> nothing to do
else
vectorCellTypeMap + (dfType -> None) // used in more than one block -> global named type
case Some(None) => vectorCellTypeMap // known to be a global type
// found new named type
case None =>
// if referenced by a global member -> global named type
if (dfVal.isGlobal)
vectorCellTypeMap + (dfType -> None)
else
vectorCellTypeMap + (dfType -> Some(dfVal.getOwnerDesign))
}
case (vectorCellTypeMap, _) => vectorCellTypeMap // not a value
}
end vectorCellTypes
private val vectorTypeIdx = mutable.Map.empty[DFVector, Int]
dt :: flatten(dt.cellType)
case _ => Nil
ListMap.from(
dcls.flatMap(dcl => flatten(dcl.dfType).reverse)
.map(getVecDepthAndCellTypeName)
.foldLeft(ListMap.empty[String, Int]) { case (listMap, (cellTypeName, depth)) =>
listMap.updatedWith(cellTypeName)(prevDepthOpt =>
Some(prevDepthOpt.getOrElse(0).max(depth))
)
}
)
end getVectorTypes
lazy val globalVectorTypes: ListMap[String, Int] =
getVectorTypes(
getSet.designDB.members.view.collect {
case port @ DclPort() => port
case const @ DclConst() if const.isGlobal => const
}
)
def getLocalVectorTypes(design: DFDesignBlock): ListMap[String, Int] =
getVectorTypes(
getSet.designDB.designMemberTable(design).view.collect {
case localVar @ DclVar() => localVar
case localConst @ DclConst() => localConst
}
)
@tailrec private def getVecDepthAndCellTypeName(dfType: DFVector, depth: Int): (String, Int) =
dfType.cellType match
case dfType: DFVector => getVecDepthAndCellTypeName(dfType, depth + 1)
case cellType => (csDFType(cellType, true), depth)
def getVecDepthAndCellTypeName(dfType: DFVector): (String, Int) =
getVecDepthAndCellTypeName(dfType, 1)

def csDFVectorDclName(cellTypeName: String, depth: Int): String =
s"t_vecX${depth}_${cellTypeName}"
def csDFVectorDclName(dfType: DFVector): String =
s"t_array_elem${vectorTypeIdx(dfType)}"
def csDFVectorDcl(dfType: DFVector): String =
s"type ${csDFVectorDclName(dfType)} is array (natural range <>) of ${csDFType(dfType.cellType, false)}"
val (cellTypeName, depth) = getVecDepthAndCellTypeName(dfType)
csDFVectorDclName(cellTypeName, depth)
def csDFVectorDcl(cellTypeName: String, depth: Int): String =
val ofTypeName = if (depth == 1) cellTypeName else csDFVectorDclName(cellTypeName, depth - 1)
s"type ${csDFVectorDclName(cellTypeName, depth)} is array (natural range <>) of $ofTypeName;"
def csDFVectorDcls(cellTypeName: String, depth: Int, start: Int): String =
(for (i <- start to depth) yield csDFVectorDcl(cellTypeName, i))
.mkString("\n")
def csDFVectorDclsGlobal(cellTypeName: String, depth: Int): String =
csDFVectorDcls(cellTypeName, depth, 1)
def csDFVectorDclsLocal(cellTypeName: String, depth: Int): String =
csDFVectorDcls(cellTypeName, depth, globalVectorTypes.getOrElse(cellTypeName, 0) + 1)
def csDFVector(dfType: DFVector, typeCS: Boolean): String =
import dfType.*
val idx = vectorTypeIdx.get(dfType) match
case Some(idx) => idx
case _ =>
val idx = vectorTypeIdx.size
vectorTypeIdx += dfType -> idx
idx
s"${csDFVectorDclName(dfType)}(0 to ${cellDims.head} - 1)"
if (typeCS) csDFVectorDclName(dfType)
else
var loopType: DFType = dfType
var desc: String = csDFVectorDclName(dfType)
var inVector: Boolean = true
while (inVector)
loopType match
case dfType: DFVector =>
desc = desc + s"(0 to ${dfType.cellDims.head} - 1)"
loopType = dfType.cellType
case cellType =>
val finale = cellType match
case DFBits(width) => s"(${width.uboundCS} downto 0)"
case DFUInt(width) => s"(${width.uboundCS} downto 0)"
case DFSInt(width) => s"(${width.uboundCS} downto 0)"
case _ => ""
desc = desc + finale
inVector = false
desc
end csDFVector
def csDFOpaqueTypeName(dfType: DFOpaque): String = s"t_opaque_${dfType.getName}"
def csDFOpaqueDcl(dfType: DFOpaque): String =
s"subtype ${csDFOpaqueTypeName(dfType)} is ${csDFType(dfType.actualType)};"
Expand All @@ -97,53 +130,53 @@ protected trait VHDLTypePrinter extends AbstractTypePrinter:
def csDFStruct(dfType: DFStruct, typeCS: Boolean): String = csDFStructTypeName(dfType)
def csDFStructConvFuncsDcl(dfType: DFStruct): String =
val typeName = csDFStructTypeName(dfType)
s"""|function get_length(A: ${typeName}) return integer;
s"""|function bitWidth(A: ${typeName}) return integer;
|function to_slv(A: ${typeName}) return std_logic_vector;
|function to_${typeName}(A: std_logic_vector) return ${typeName};""".stripMargin
def csDFStructConvFuncsBody(dfType: DFStruct): String =
val typeName = csDFStructTypeName(dfType)
def getLengthFunc(dfType: DFType, csArg: String): String = dfType match
def bitWidthFunc(dfType: DFType, csArg: String): String = dfType match
case DFBits(_) | DFUInt(_) | DFSInt(_) => s"$csArg'length"
case _: DFBoolOrBit => "1"
case DFVector(cellType, cellDims) =>
s"${cellDims.head} * ${getLengthFunc(cellType, s"$csArg(0)")}"
case dfType: DFStruct => s"get_length($csArg)"
case dfType: DFOpaque => getLengthFunc(dfType.actualType, csArg)
s"${cellDims.head} * ${bitWidthFunc(cellType, s"$csArg(0)")}"
case dfType: DFStruct => s"bitWidth($csArg)"
case dfType: DFOpaque => bitWidthFunc(dfType.actualType, csArg)
case _ => printer.unsupported
def to_slv(fromType: DFType, csArg: String): String = fromType match
case DFBits(_) => csArg
case _ => s"to_slv($csArg)"
val fieldLengths = dfType.fieldMap.map { (n, t) =>
s"len := len + ${getLengthFunc(t, s"A.$n")};"
s"len := len + ${bitWidthFunc(t, s"A.$n")};"
}.mkString("\n ")
val vecAssignments = dfType.fieldMap.map { (n, t) =>
s"lo := hi + 1; hi := lo + ${getLengthFunc(t, s"A.$n")} - 1; ret(hi downto lo) := ${to_slv(t, s"A.$n")};"
s"hi := lo - 1; lo := hi - ${bitWidthFunc(t, s"A.$n")} + 1; ret(hi downto lo) := ${to_slv(t, s"A.$n")};"
}.mkString("\n ")
val fieldAssignments = dfType.fieldMap.map { (n, t) =>
s"lo := hi + 1; hi := lo + ${getLengthFunc(t, s"ret.$n")} - 1; ret.$n := ${printer.csBitsToType(t, "A(hi downto lo)")};"
s"hi := lo - 1; lo := hi - ${bitWidthFunc(t, s"ret.$n")} + 1; ret.$n := ${printer.csBitsToType(t, "A(hi downto lo)")};"
}.mkString("\n ")
s"""|function get_length(A: ${typeName}) return integer is
s"""|function bitWidth(A : ${typeName}) return integer is
| variable len : integer;
|begin
| len := 0;
| ${fieldLengths}
| return len;
|end;
|function to_slv(A: ${typeName}) return std_logic_vector is
|function to_slv(A : ${typeName}) return std_logic_vector is
| variable hi : integer;
| variable lo : integer;
| variable ret : std_logic_vector(get_length(A) - 1 downto 0);
| variable ret : std_logic_vector(bitWidth(A) - 1 downto 0);
|begin
| hi := -1;
| lo := bitWidth(A);
| ${vecAssignments}
| return ret;
|end;
|function to_${typeName}(A: std_logic_vector) return ${typeName} is
|function to_${typeName}(A : std_logic_vector) return ${typeName} is
| variable hi : integer;
| variable lo : integer;
| variable ret : ${typeName};
|begin
| hi := -1;
| lo := bitWidth(A);
| ${fieldAssignments}
| return ret;
|end;""".stripMargin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,12 @@ protected trait VHDLValPrinter extends AbstractValPrinter:
.map { case ((n, _), d) => s"$n = $d" }
.mkStringBrackets

case DFVector(_, _) => printer.unsupported
// all args are the same ==> repeat function
case _ if args.view.map(_.get).allElementsAreEqual =>
s"repeat(${args.head.refCodeString},${args.length})"
case _ if args.view.map(_.get).forall(_ =~ args.head.get) =>
s"(0 to ${args.length}-1 => ${args.head.refCodeString.applyBrackets()})"

case DFVector(_, _) =>
args.map(_.refCodeString).mkStringBrackets
// regular concatenation function
case _ => args.map(_.refCodeString).mkString(" & ")
end match
Expand Down
62 changes: 47 additions & 15 deletions compiler/stages/src/test/scala/StagesSpec/PrintVHDLCodeSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -258,21 +258,22 @@ class PrintVHDLCodeSpec extends StageSpec:
}
test("literals") {
class Top extends EDDesign:
val c01: Bit <> CONST = 0
val c02: Bit <> CONST = 1
val c03: Bit <> CONST = ?
val c04: Boolean <> CONST = false
val c05: Boolean <> CONST = true
val c06: Bits[8] <> CONST = h"22"
val c07: Bits[7] <> CONST = h"7'22"
val c08: Bits[3] <> CONST = b"101"
val c09: UInt[3] <> CONST = 7
val c10: UInt[48] <> CONST = d"48'239794508230343"
val c11: SInt[4] <> CONST = -8
val c12: SInt[49] <> CONST = sd"49'-239794508230343"
val c13: UInt[8] <> CONST = ?
val c14: SInt[8] <> CONST = ?
val c15: (Bits[3], Bit) <> CONST = (all(0), 1)
val c01: Bit <> CONST = 0
val c02: Bit <> CONST = 1
val c03: Bit <> CONST = ?
val c04: Boolean <> CONST = false
val c05: Boolean <> CONST = true
val c06: Bits[8] <> CONST = h"22"
val c07: Bits[7] <> CONST = h"7'22"
val c08: Bits[3] <> CONST = b"101"
val c09: UInt[3] <> CONST = 7
val c10: UInt[48] <> CONST = d"48'239794508230343"
val c11: SInt[4] <> CONST = -8
val c12: SInt[49] <> CONST = sd"49'-239794508230343"
val c13: UInt[8] <> CONST = ?
val c14: SInt[8] <> CONST = ?
val c15: (Bits[3], Bit) <> CONST = (all(0), 1)
val c16: Bits[8] X 5 X 7 <> CONST = Vector.fill(7)(Vector.tabulate(5)(i => h"8'$i$i"))
end Top
val top = (new Top).getCompiledCodeString
assertNoDiff(
Expand All @@ -290,6 +291,36 @@ class PrintVHDLCodeSpec extends StageSpec:
| _1 : std_logic_vector(2 downto 0);
| _2 : std_logic;
| end record;
| type t_vecX1_std_logic_vector is array (natural range <>) of std_logic_vector;
| type t_vecX2_std_logic_vector is array (natural range <>) of t_vecX1_std_logic_vector;
| function bitWidth(A : t_struct_DFTuple2) return integer is
| variable len : integer;
| begin
| len := 0;
| len := len + A._1'length;
| len := len + 1;
| return len;
| end;
| function to_slv(A : t_struct_DFTuple2) return std_logic_vector is
| variable hi : integer;
| variable lo : integer;
| variable ret : std_logic_vector(bitWidth(A) - 1 downto 0);
| begin
| lo := bitWidth(A);
| hi := lo - 1; lo := hi - A._1'length + 1; ret(hi downto lo) := A._1;
| hi := lo - 1; lo := hi - 1 + 1; ret(hi downto lo) := to_slv(A._2);
| return ret;
| end;
| function to_t_struct_DFTuple2(A : std_logic_vector) return t_struct_DFTuple2 is
| variable hi : integer;
| variable lo : integer;
| variable ret : t_struct_DFTuple2;
| begin
| lo := bitWidth(A);
| hi := lo - 1; lo := hi - ret._1'length + 1; ret._1 := A(hi downto lo);
| hi := lo - 1; lo := hi - 1 + 1; ret._2 := to_sl(A(hi downto lo));
| return ret;
| end;
| constant c01 : std_logic := '0';
| constant c02 : std_logic := '1';
| constant c03 : std_logic := '-';
Expand All @@ -305,6 +336,7 @@ class PrintVHDLCodeSpec extends StageSpec:
| constant c13 : unsigned(7 downto 0) := unsigned'(x"--");
| constant c14 : signed(7 downto 0) := signed'(x"--");
| constant c15 : t_struct_DFTuple2 := t_struct_DFTuple2(_1 = "000", _2 = '1');
| constant c16 : t_vecX2_std_logic_vector(0 to 7 - 1)(0 to 5 - 1)(7 downto 0) := (0 to 7-1 => (x"00", x"11", x"22", x"33", x"44"));
|begin
|
|end Top_arch;
Expand Down
Loading

0 comments on commit b75879b

Please sign in to comment.