Skip to content

Commit c5bf81a

Browse files
author
Oron Port
committed
handle more verilog 95/2001 vector to bits conversion cases
1 parent e37977a commit c5bf81a

File tree

11 files changed

+1258
-98
lines changed

11 files changed

+1258
-98
lines changed

compiler/ir/src/main/scala/dfhdl/compiler/printing/DFValPrinter.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ trait AbstractValPrinter extends AbstractPrinter:
111111
final def csDFValDcl(dfVal: Dcl): String =
112112
val noInit = csDFValDclWithoutInit(dfVal)
113113
val init = dfVal.initRefList match
114-
// case DFRef(DFVector.Val(_)) :: _ if !printer.supportVectorInlineInit => ""
115-
case _
116-
if dfVal.dfType.isInstanceOf[DFVector] && !printer.supportVectorInlineInit => ""
117-
case DFRef(DFVal.Func(op = FuncOp.InitFile(format, path))) :: Nil =>
114+
case DFRef(DFVector.Val(_)) :: _ if !printer.supportVectorInlineInit => ""
115+
case DFRef(DFVal.Func(op = FuncOp.InitFile(format, path))) :: Nil =>
118116
val csInitFile = format match
119117
case InitFileFormat.Auto => s""""$path""""
120118
case _ => s"""("$path", InitFileFormat.$format)"""
@@ -186,8 +184,8 @@ protected trait DFValPrinter extends AbstractValPrinter:
186184
// repeat func
187185
case argL :: argR :: Nil if dfVal.op == Func.Op.repeat =>
188186
dfVal.dfType match
189-
case dfType: DFVector => s"DFVector(${printer.csDFType(dfType)})(${argL.refCodeString})"
190-
case _ =>
187+
case _: DFVector => s"all(${argL.refCodeString})"
188+
case _ =>
191189
val csArgL = argL.refCodeString(typeCS)
192190
val csArgR = argR.refCodeString(typeCS)
193191
s"${csArgL.applyBrackets()}.repeat${csArgR.applyBrackets(onlyIfRequired = false)}"

compiler/stages/src/main/scala/dfhdl/compiler/stages/DropStructsVecs.scala

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import dfhdl.compiler.patching.*
66
import dfhdl.options.CompilerOptions
77
import dfhdl.compiler.stages.verilog.VerilogDialect
88
import dfhdl.core.DFType.asFE
9-
import dfhdl.core.{DFTypeAny, widthIntParam, IntParam, get}
10-
import dfhdl.compiler.ir.DFVal.Alias.ApplyIdx
9+
import dfhdl.core.{DFTypeAny, widthIntParam, IntParam, get, DFValAny}
10+
import dfhdl.compiler.ir.DFType as irDFType
11+
import dfhdl.compiler.ir.DFVector as irDFVector
1112
import scala.collection.mutable
13+
import dfhdl.core.DFVal.Func.Op as FuncOp
1214

1315
/** This stage drops all structs and (non-Bit) vectors that are not standard block-ram accesses. It
1416
* drops them by flattening them into Bits.
@@ -21,23 +23,22 @@ case object DropStructsVecs extends Stage:
2123
case VerilogDialect.v95 | VerilogDialect.v2001 => true
2224
case _ => false
2325
case _ => false
24-
override def dependencies: List[Stage] = List()
25-
override def nullifies: Set[Stage] = Set(DropUnreferencedAnons, ExplicitRomVar)
26+
override def dependencies: List[Stage] = List(ExplicitRomVar)
27+
override def nullifies: Set[Stage] = Set(DropUnreferencedAnons)
2628
def transform(designDB: DB)(using MemberGetSet, CompilerOptions): DB =
2729
given RefGen = RefGen.fromGetSet
2830
object StructOrVecVal:
2931
def unapply(dfVal: DFVal)(using MemberGetSet): Boolean = dfVal.dfType match
3032
// all structs are dropped
3133
case _: DFStruct => true
32-
// all vectors are dropped, except for var with block-ram and an initial value cast to vector
34+
// all vectors are dropped, except for var with block-ram.
35+
// also anonymous initial vector values are specially handled within the declaration
36+
// patch, so we skip over them here
3337
case _: DFVector =>
3438
dfVal match
35-
case BlockRamVar() => false
36-
case initialVal @ InitialValueOf(BlockRamVar()) =>
37-
initialVal match
38-
case initVal: DFVal.Alias.AsIs if initVal.isAnonymous => false
39-
case _ => true
40-
case _ => true
39+
// case BlockRamVar() => false
40+
case InitialValueOf(_) if dfVal.isAnonymous => false
41+
case _ => true
4142
case _ => false
4243
end StructOrVecVal
4344
val replacementMap = mutable.Map.empty[DFVal, DFVal]
@@ -56,16 +57,85 @@ case object DropStructsVecs extends Stage:
5657
dfVal,
5758
Patch.Add.Config.ReplaceWithLast(Patch.Replace.Config.FullReplacement)
5859
):
59-
val width = dfVal.dfType.asFE[DFTypeAny].widthIntParam
60-
val updatedDFType = DFBits(width.ref)
60+
def updateArg(arg: DFVal): DFValAny = arg.dfType match
61+
// Structs and Vectors will be replaced with Bits in a different patch
62+
case _: (DFStruct | DFVector | DFBits) => arg.asValAny
63+
case _ if !arg.isAnonymous => arg.asValAny.bits
64+
case _ => recurToBits(arg).asValAny
65+
def typeToBits(dfType: irDFType): DFTypeAny =
66+
val width = dfType.asFE[DFTypeAny].widthIntParam
67+
DFBits(width.ref).asFE[DFTypeAny]
68+
def recurToBits(dfVal: DFVal): DFVal =
69+
val updatedDFType = typeToBits(dfVal.dfType)
70+
dfVal match
71+
// update constant data to bits
72+
case const: DFVal.Const =>
73+
val updatedData =
74+
const.dfType.dataToBitsData(const.data.asInstanceOf[const.dfType.Data])
75+
dfhdl.core.DFVal.Const.forced(updatedDFType, updatedData)(using
76+
dfc.setMeta(const.meta)
77+
).asIR
78+
// update vector concatenation arguments to bits as well
79+
case concat @ DFVal.Func(op = FuncOp.++, args = args) =>
80+
val updatedArgs = args.map(a => updateArg(a.get))
81+
dfhdl.core.DFVal.Func(updatedDFType, FuncOp.++, updatedArgs)(using
82+
dfc.setMeta(concat.meta)
83+
).asIR
84+
// update vector repeated argument to bits as well
85+
case repeat @ DFVal.Func(
86+
op = FuncOp.repeat,
87+
args = repeatedArgRef :: repeatCntArgRef :: Nil
88+
) =>
89+
val repeatedArg = repeatedArgRef.get
90+
val updatedArgs = List(updateArg(repeatedArg), repeatCntArgRef.get.asValAny)
91+
dfhdl.core.DFVal.Func(updatedDFType, FuncOp.repeat, updatedArgs)(using
92+
dfc.setMeta(repeat.meta)
93+
).asIR
94+
// for other values, just update the DFType
95+
case _ => plantMember(dfVal.updateDFType(updatedDFType.asIR))
96+
end match
97+
end recurToBits
98+
// memoize the block ram variable check
99+
val isBlockRamVar = dfVal match
100+
case BlockRamVar() => true
101+
case _ => false
61102
val updatedDFVal = dfVal match
62-
case const: DFVal.Const =>
63-
val updatedData =
64-
const.dfType.dataToBitsData(const.data.asInstanceOf[const.dfType.Data])
65-
const.copy(dfType = updatedDFType, data = updatedData)
66-
case _ => dfVal.updateDFType(updatedDFType)
67-
replacementMap += (updatedDFVal -> dfVal)
68-
plantMember(updatedDFVal)
103+
// declarations are special cased to handle the initial value
104+
case dcl: DFVal.Dcl if dcl.initRefList.nonEmpty || isBlockRamVar =>
105+
val updatedDclType =
106+
// for block ram variables, we keep the type as-is, unless the cell type is a struct or vector,
107+
// in which case we convert it to bits.
108+
if (isBlockRamVar) dcl.dfType match
109+
case dfType @ irDFVector(cellType = cellType: (DFVector | DFStruct)) =>
110+
dfType.copy(cellType = typeToBits(cellType).asIR)
111+
case dfType => dfType
112+
else typeToBits(dcl.dfType).asIR
113+
// block ram variables are not replaced with bits, because they are handled
114+
// by the verilog backend, but the initial value is converted to bits. so we
115+
// need to cast it back to the block ram vector type for the IR to be legal,
116+
// and later ignore the casting in the verilog backend.
117+
def toVector(initVal: DFVal): DFVal =
118+
if (isBlockRamVar) dfhdl.core.DFVal.Alias.AsIs.forced(updatedDclType, initVal)
119+
else initVal
120+
val updatedInits = dcl.initRefList.view.map(_.get).map {
121+
// for block ram variables, this initial value appears to be already casting from
122+
// bits to vector, so it is already in the correct type.
123+
case asIs: DFVal.Alias.AsIs if asIs.isAnonymous && isBlockRamVar => asIs
124+
// anonymous initial values are converted to bits, and maybe cast back to vector,
125+
// if the declaration is a block ram variable (see `toVector`).
126+
case initVal if initVal.isAnonymous => toVector(recurToBits(initVal))
127+
// named initial values only may need to be cast to vector (see `toVector`).
128+
case initVal => toVector(initVal)
129+
}.map(_.asConstAny).toList
130+
val modifier = new dfhdl.core.Modifier(dcl.modifier)
131+
dfhdl.core.DFVal.Dcl(updatedDclType.asFE[DFTypeAny], modifier, updatedInits)(using
132+
dfc.setMeta(dcl.meta)
133+
).asIR
134+
case _ => recurToBits(dfVal)
135+
// block ram variables are not replaced with bits, because they are handled
136+
// by the verilog backend
137+
if (!isBlockRamVar)
138+
replacementMap += (updatedDFVal -> dfVal)
69139
dsn.patch
70140
}
71141
val stage1 = designDB.patch(patchList)

compiler/stages/src/main/scala/dfhdl/compiler/stages/NamedAliases.scala

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import dfhdl.options.CompilerOptions
88
import DFVal.Func.Op as FuncOp
99
import dfhdl.compiler.stages.vhdl.VHDLDialect
1010
import dfhdl.compiler.ir.DFConditional.DFMatchHeader
11+
import dfhdl.compiler.stages.verilog.VerilogDialect
1112

1213
// Names an anonymous relative value which is aliased.
1314
// The aliasing is limited according to the criteria provided
1415
private abstract class NamedAliases extends Stage:
1516
def dependencies: List[Stage] = Nil
1617
def nullifies: Set[Stage] =
1718
Set(DFHDLUniqueNames, DropLocalDcls, ExplicitNamedVars, DropUnreferencedAnons)
18-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal]
19+
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal]
1920
def transform(designDB: DB)(using MemberGetSet, CompilerOptions): DB =
2021

2122
val patchList: List[(DFMember, Patch)] =
@@ -81,34 +82,44 @@ case object NamedVerilogSelection extends NamedAliases:
8182
else false
8283
case _ => false
8384
end extension
84-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal] = dfVal match
85-
case alias: DFVal.Alias if alias.relValRef.get.hasVerilogName => Nil
86-
case alias: DFVal.Alias.ApplyRange if alias.width != alias.relValRef.get.width =>
87-
List(alias.relValRef.get)
88-
case alias: DFVal.Alias.AsIs if alias.width < alias.relValRef.get.width =>
89-
if (alias.relValRef.get.dfType == DFInt32)
90-
Nil // conversion from DFInt32 is not a bit selection, so no need to break the expression
91-
else List(alias.relValRef.get)
92-
// to/from vector conversion is used with selection
93-
case DFVal.Alias.AsIs(dfType = DFVector(_, _), relValRef = DFRef(relVal @ DFBits.Val(_))) =>
94-
List(relVal)
95-
case DFVal.Alias.AsIs(dfType = DFBits(_), relValRef = DFRef(relVal @ DFVector.Val(_))) =>
96-
List(relVal)
97-
case alias: DFVal.Alias.ApplyIdx =>
98-
List(alias.relValRef.get)
99-
case func @ DFVal.Func(op = op, args = DFRef(lhs) :: _ :: Nil)
100-
if !lhs.hasVerilogName && carryOps.contains(op) && func.width > lhs.width =>
101-
List(lhs)
102-
// anonymous conditional expressions
103-
case ch: DFConditional.Header if ch.isAnonymous && ch.dfType != DFUnit =>
104-
ch.getReadDeps.head match
105-
// if the conditional is referred from a net, it is not a selection to be named
106-
case net: DFNet => Nil
107-
// if the conditional is referred from an ident, it is not a selection to be named
108-
case Ident(_) => Nil
109-
// otherwise, it is a selection to be named
110-
case _ => List(ch)
111-
case _ => Nil
85+
def criteria(dfVal: DFVal)(using getSet: MemberGetSet, co: CompilerOptions): List[DFVal] =
86+
dfVal match
87+
case alias: DFVal.Alias if alias.relValRef.get.hasVerilogName => Nil
88+
case alias: DFVal.Alias.ApplyRange if alias.width != alias.relValRef.get.width =>
89+
List(alias.relValRef.get)
90+
case alias: DFVal.Alias.AsIs if alias.width < alias.relValRef.get.width =>
91+
if (alias.relValRef.get.dfType == DFInt32)
92+
Nil // conversion from DFInt32 is not a bit selection, so no need to break the expression
93+
else List(alias.relValRef.get)
94+
// to/from vector conversion is used with selection
95+
case DFVal.Alias.AsIs(dfType = DFVector(_, _), relValRef = DFRef(relVal @ DFBits.Val(_))) =>
96+
// in basic verilog this casting is only kept for initial values and later ignored by the backend
97+
val isBasicVerilog = co.backend match
98+
case be: dfhdl.backends.verilog =>
99+
be.dialect match
100+
case VerilogDialect.v95 | VerilogDialect.v2001 => true
101+
case _ => false
102+
case _ => false
103+
// preventing basic verilog compilation from naming the casted value
104+
if (isBasicVerilog) Nil
105+
else List(relVal)
106+
case DFVal.Alias.AsIs(dfType = DFBits(_), relValRef = DFRef(relVal @ DFVector.Val(_))) =>
107+
List(relVal)
108+
case alias: DFVal.Alias.ApplyIdx =>
109+
List(alias.relValRef.get)
110+
case func @ DFVal.Func(op = op, args = DFRef(lhs) :: _ :: Nil)
111+
if !lhs.hasVerilogName && carryOps.contains(op) && func.width > lhs.width =>
112+
List(lhs)
113+
// anonymous conditional expressions
114+
case ch: DFConditional.Header if ch.isAnonymous && ch.dfType != DFUnit =>
115+
ch.getReadDeps.head match
116+
// if the conditional is referred from a net, it is not a selection to be named
117+
case net: DFNet => Nil
118+
// if the conditional is referred from an ident, it is not a selection to be named
119+
case Ident(_) => Nil
120+
// otherwise, it is a selection to be named
121+
case _ => List(ch)
122+
case _ => Nil
112123
end NamedVerilogSelection
113124

114125
// For vhdl patten matching of a selection is limited.
@@ -120,7 +131,7 @@ case object NamedVHDLSelection extends NamedAliases:
120131
case VHDLDialect.v93 => true
121132
case _ => false
122133
case _ => false
123-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal] =
134+
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal] =
124135
dfVal.getReadDeps.headOption match
125136
case Some(_: DFConditional.DFMatchHeader) => List(dfVal)
126137
case _ => Nil
@@ -133,7 +144,7 @@ extension [T: HasDB](t: T)
133144
// Creating a previous values of a value requires that value to be names to avoid random anonymous names in the
134145
// the backend
135146
case object NamedPrev extends NamedAliases:
136-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal] = dfVal match
147+
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal] = dfVal match
137148
case alias: DFVal.Alias.History if alias.relValRef.get.isAnonymous =>
138149
List(alias.relValRef.get)
139150
case _ => Nil
@@ -143,7 +154,7 @@ extension [T: HasDB](t: T)
143154

144155
// Names an anonymous value which is referenced more than once
145156
case object NamedAnonMultiref extends NamedAliases, NoCheckStage:
146-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal] = dfVal match
157+
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal] = dfVal match
147158
case dfVal if !dfVal.isAnonymous => Nil
148159
case dfVal =>
149160
// referenced more than once (excluding else/case blocks referencing their headers & type refs)
@@ -159,7 +170,7 @@ case object NamedAnonMultiref extends NamedAliases, NoCheckStage:
159170
//they are themselves inside another conditional expression
160171
case object NamedAnonCondExpr extends NamedAliases:
161172
override def dependencies: List[Stage] = List(ExplicitCondExprAssign)
162-
def criteria(dfVal: DFVal)(using MemberGetSet): List[DFVal] = dfVal match
173+
def criteria(dfVal: DFVal)(using MemberGetSet, CompilerOptions): List[DFVal] = dfVal match
163174
case dfVal: DFConditional.Header if dfVal.isAnonymous && dfVal.dfType != DFUnit =>
164175
val isReferencedByIdent =
165176
dfVal.getReadDeps.collectFirst { case Ident(_) => true }.getOrElse(false)

compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogOwnerPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ protected trait VerilogOwnerPrinter extends AbstractOwnerPrinter:
6767
case IteratorDcl() => None
6868
case p: DFVal.Dcl if p.isVar || !parameterizedModuleSupport =>
6969
p.dfType match
70-
case _: DFVector if p.initRefList.nonEmpty =>
70+
case _: DFVector if !printer.supportVectorInlineInit && p.initRefList.nonEmpty =>
7171
List(printer.csDFMember(p) + ";", printer.csDFValDclInitialBlock(p))
7272
case _ => List(printer.csDFMember(p) + ";")
7373
case _: DesignParam => None

compiler/stages/src/main/scala/dfhdl/compiler/stages/verilog/VerilogValPrinter.scala

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,26 @@ protected trait VerilogValPrinter extends AbstractValPrinter:
5959
def csInitSeq(refs: List[Dcl.InitRef]): String = printer.unsupported
6060
def csDFValDclEnd(dfVal: Dcl): String = ""
6161
def csDFValDclInitialBlock(dfVal: Dcl): String =
62-
val initVal = dfVal.initRefList.head.get
63-
val dfType = dfVal.dfType.asInstanceOf[DFVector]
64-
val length = dfType.cellDimParamRefs.head.refCodeString
65-
val cellWidth = dfType.cellType.width
66-
val contents = if (initVal.isAnonymous)
67-
???
68-
else
69-
s"""|integer i;
70-
|for (i = 0; i < ${length}; i = i + 1) begin
71-
| ${dfVal.getName}[i] = ${initVal.getName}[($length-i)*${cellWidth}-1-:${cellWidth}];
72-
|end""".stripMargin
62+
val List(DFRef(DFVal.Alias.AsIs(dfType = dfType: DFVector, relValRef = DFRef(initVal)))) =
63+
dfVal.initRefList: @unchecked
64+
val contents = initVal match
65+
case _ if !initVal.isAnonymous =>
66+
val cellWidth = dfType.cellType.width
67+
val length = dfType.cellDimParamRefs.head.getInt
68+
val ret = for (i <- 0 until length)
69+
yield s"${dfVal.getName}[$i] = ${initVal.getName}[${(length - i) * cellWidth - 1}:${(length - i) * cellWidth - cellWidth}];"
70+
ret.mkString("\n")
71+
case Func(op = Func.Op.++, args = args) =>
72+
args.view.zipWithIndex
73+
.map((a, i) => s"${dfVal.getName}[$i] = ${a.refCodeString};")
74+
.mkString("\n")
75+
case Func(op = Func.Op.repeat, args = repeatedArgRef :: _) =>
76+
val length = dfType.cellDimParamRefs.head.refCodeString
77+
s"""|integer i;
78+
|for (i = 0; i < ${length}; i = i + 1) begin
79+
| ${dfVal.getName}[i] = ${repeatedArgRef.refCodeString};
80+
|end""".stripMargin
81+
case _ => printer.unsupported
7382
s"""|initial begin : ${dfVal.getName}_init
7483
|${contents.hindent}
7584
|end""".stripMargin

0 commit comments

Comments
 (0)