@@ -6,9 +6,11 @@ import dfhdl.compiler.patching.*
6
6
import dfhdl .options .CompilerOptions
7
7
import dfhdl .compiler .stages .verilog .VerilogDialect
8
8
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
11
12
import scala .collection .mutable
13
+ import dfhdl .core .DFVal .Func .Op as FuncOp
12
14
13
15
/** This stage drops all structs and (non-Bit) vectors that are not standard block-ram accesses. It
14
16
* drops them by flattening them into Bits.
@@ -21,23 +23,22 @@ case object DropStructsVecs extends Stage:
21
23
case VerilogDialect .v95 | VerilogDialect .v2001 => true
22
24
case _ => false
23
25
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 )
26
28
def transform (designDB : DB )(using MemberGetSet , CompilerOptions ): DB =
27
29
given RefGen = RefGen .fromGetSet
28
30
object StructOrVecVal :
29
31
def unapply (dfVal : DFVal )(using MemberGetSet ): Boolean = dfVal.dfType match
30
32
// all structs are dropped
31
33
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
33
37
case _ : DFVector =>
34
38
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
41
42
case _ => false
42
43
end StructOrVecVal
43
44
val replacementMap = mutable.Map .empty[DFVal , DFVal ]
@@ -56,16 +57,85 @@ case object DropStructsVecs extends Stage:
56
57
dfVal,
57
58
Patch .Add .Config .ReplaceWithLast (Patch .Replace .Config .FullReplacement )
58
59
):
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
61
102
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)
69
139
dsn.patch
70
140
}
71
141
val stage1 = designDB.patch(patchList)
0 commit comments