Skip to content

Commit 22b548b

Browse files
authored
Merge pull request #276 from DFiantHDL/vivado_build
v0.12.0
2 parents fcaad78 + c582784 commit 22b548b

File tree

235 files changed

+7787
-3342
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

235 files changed

+7787
-3342
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ jobs:
4444
ghdl version
4545
nvc --version
4646
- name: Run tests
47-
run: sbt test
47+
run: sbt test
48+
- name: Run app tests
49+
run: sbt testApps

.scalafmt.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = 3.9.7
1+
version = 3.9.8
22
runner.dialect = scala3
33

44
maxColumn = 100

build.sbt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
commands += DFHDLCommands.quickTestSetup
2+
commands += DFHDLCommands.clearSandbox
3+
commands += DFHDLCommands.testApps
24
commands += DFHDLCommands.docExamplesRefUpdate
35

46
// format: off
@@ -77,7 +79,7 @@ lazy val core = project
7779
name := s"$projectName-core",
7880
settings,
7981
pluginTestUseSettings,
80-
libraryDependencies ++= commonDependencies :+ dependencies.scalafmt,
82+
libraryDependencies ++= commonDependencies,
8183
Compile / resourceGenerators += Def.task {
8284
val file = (Compile / resourceManaged).value / "version.properties"
8385
val contents = s"version=${version.value}"
@@ -136,17 +138,15 @@ lazy val devices = (project in file("devices"))
136138

137139
lazy val dependencies =
138140
new {
139-
private val scodecV = "1.2.1"
141+
private val scodecV = "1.2.4"
140142
private val munitV = "1.1.1"
141-
private val scalafmtV = "3.8.3"
142-
private val airframelogV = "2025.1.12"
143+
private val airframelogV = "2025.1.14"
143144
private val oslibV = "0.9.2"
144145
private val scallopV = "5.2.0"
145146
private val upickleV = "4.2.1"
146147

147148
val scodec = "org.scodec" %% "scodec-bits" % scodecV
148149
val munit = "org.scalameta" %% "munit" % munitV % Test
149-
val scalafmt = ("org.scalameta" %% "scalafmt-dynamic" % scalafmtV).cross(CrossVersion.for3Use2_13)
150150
val airframelog = "org.wvlet.airframe" %% "airframe-log" % airframelogV
151151
val oslib = "com.lihaoyi" %% "os-lib" % oslibV
152152
val scallop = "org.rogach" %% "scallop" % scallopV

compiler/ir/src/main/scala/dfhdl/compiler/analysis/DFValAnalysis.scala

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,25 @@ object PortOfDesignDef:
171171
else None
172172
case _ => None
173173

174+
object InitialValueOf:
175+
def unapply(initVal: DFVal)(using MemberGetSet): Option[DFVal.Dcl] =
176+
initVal.originMembersNoTypeRef.collectFirst {
177+
case dcl: DFVal.Dcl if dcl.initRefList.map(_.get).contains(initVal) => dcl
178+
}
179+
180+
object BlockRamVar:
181+
def unapply(dfVal: DFVal)(using MemberGetSet): Boolean = dfVal.dfType match
182+
case dfType: DFVector =>
183+
dfVal match
184+
case DclVar() =>
185+
// if the var only has index accesses, then it's a block-ram access.
186+
dfVal.getReadDeps.forall {
187+
case applyIdx: DFVal.Alias.ApplyIdx => true
188+
case _ => false
189+
}
190+
case _ => false
191+
case _ => false
192+
174193
extension (ref: DFRef.TwoWayAny)
175194
def originMember(using MemberGetSet): DFMember =
176195
getSet.getOrigin(ref)
@@ -252,10 +271,10 @@ extension (dfVal: DFVal)
252271
@tailrec private def flatName(member: DFVal, suffix: String)(using MemberGetSet): String =
253272
member match
254273
case named if !named.isAnonymous => s"${member.getName}$suffix"
255-
case alias: DFVal.Alias.Partial =>
274+
case alias: DFVal.Alias.Partial =>
256275
val relVal = alias.relValRef.get
257276
val newSuffix = alias match
258-
case _: DFVal.Alias.AsIs => suffix
277+
case _: DFVal.Alias.AsIs => suffix
259278
case applyIdx: DFVal.Alias.ApplyIdx =>
260279
applyIdx.relIdx.get match
261280
case DFVal.Alias.ApplyIdx.ConstIdx(i) =>
@@ -266,7 +285,15 @@ extension (dfVal: DFVal)
266285
s"_${i.toPaddedString(maxValue)}"
267286
case _ => "_sel"
268287
case applyRange: DFVal.Alias.ApplyRange =>
269-
s"_${applyRange.relBitHigh.toPaddedString(applyRange.width - 1)}_${applyRange.relBitLow.toPaddedString(applyRange.width - 1)}"
288+
(applyRange.dfType: @unchecked) match
289+
case DFBits(_) =>
290+
val idxHigh = applyRange.idxHighRef.getInt.toPaddedString(applyRange.width - 1)
291+
val idxLow = applyRange.idxLowRef.getInt.toPaddedString(applyRange.width - 1)
292+
s"_${idxHigh}_${idxLow}"
293+
case dfType: DFVector =>
294+
val idxHigh = applyRange.idxHighRef.getInt.toPaddedString(dfType.length - 1)
295+
val idxLow = applyRange.idxLowRef.getInt.toPaddedString(dfType.length - 1)
296+
s"_${idxLow}_${idxHigh}"
270297
case selectField: DFVal.Alias.SelectField => s"_${selectField.fieldName}"
271298
flatName(relVal, s"$newSuffix$suffix")
272299
case _ => s"${member.getName}$suffix"
@@ -365,7 +392,14 @@ extension (dfVal: DFVal)
365392
case _: DFVal.PortByNameSelect => true // allow anonymous port by name selection
366393
case OpaqueActual(_) => true // allow anonymous opaque actual selection
367394
case _ => false
368-
395+
// true if this is a partial assignment or connection destination
396+
def isPartialNetDest(using MemberGetSet): Boolean = dfVal match
397+
case dfVal: DFVal.Alias.Partial =>
398+
dfVal.originMembers.headOption match
399+
case Some(DFNet.Assignment(toVal = toVal)) if toVal == dfVal => true
400+
case Some(DFNet.Connection(toVal = toVal)) if toVal == dfVal => true
401+
case _ => false
402+
case _ => false
369403
end extension
370404

371405
extension (refTW: DFNet.Ref)
@@ -421,8 +455,8 @@ extension (members: List[DFMember])
421455
member :: member.getRefs.flatMap { r =>
422456
val publicMemberCandidate = r.get
423457
publicMemberCandidate match
424-
case _: DFMember.Empty => Nil
425-
case dfVal: DFVal.CanBeGlobal if dfVal.isGlobal => Nil
458+
case _: DFMember.Empty => Nil
459+
case dfVal: DFVal.CanBeGlobal if dfVal.isGlobal => Nil
426460
case _ if publicMemberCandidate.isSameOwnerDesignAs(member) =>
427461
getPublicMembersDeps(publicMemberCandidate)
428462
case _ => Nil
@@ -438,9 +472,9 @@ extension (member: DFMember)
438472
member match
439473
case loop: DFLoop.Block =>
440474
loop.isInRTDomain && !loop.isCombinational
441-
case wait: Wait => wait.isInRTDomain
442-
case _: StepBlock => true
443-
case _: Goto => true
475+
case wait: Wait => wait.isInRTDomain
476+
case _: StepBlock => true
477+
case _: Goto => true
444478
case cb: DFConditional.Block =>
445479
cb.members(MemberView.Folded).exists(_.consumesCycles)
446480
case _ => false

compiler/ir/src/main/scala/dfhdl/compiler/analysis/StateAnalysis.scala

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ object StateAnalysis:
1212
relBitLow: Int,
1313
assignMap: AssignMap,
1414
currentSet: Set[DFVal]
15-
)(using MemberGetSet): Set[DFVal] =
15+
)(using MemberGetSet, DFBlock): Set[DFVal] =
16+
val currentBlock = summon[DFBlock]
1617
val access = immutable.BitSet.empty ++ (relBitLow until relBitLow + relWidth)
1718
value match
1819
case DFVal.Alias.AsIs(dfType = toType, relValRef = relValRef) =>
@@ -23,8 +24,19 @@ object StateAnalysis:
2324
else
2425
// conversion is treated like any function argument and restarts bit consumption
2526
consumeFrom(relVal, relVal.width, 0, assignMap, currentSet)
26-
case DFVal.Alias.ApplyRange(relValRef = relValRef, relBitHigh = rbh, relBitLow = rbl) =>
27-
consumeFrom(relValRef.get, rbh - rbl + 1, relBitLow + rbl, assignMap, currentSet)
27+
case applyRange @ DFVal.Alias.ApplyRange(
28+
relValRef = relValRef,
29+
idxHighRef = Int(idxHigh),
30+
idxLowRef = Int(idxLow)
31+
) =>
32+
val elementWidth = applyRange.elementWidth
33+
consumeFrom(
34+
relValRef.get,
35+
idxHigh * elementWidth - idxLow * elementWidth + 1,
36+
relBitLow + idxLow * elementWidth,
37+
assignMap,
38+
currentSet
39+
)
2840
case DFVal.Alias.ApplyIdx(relValRef = relValRef, relIdx = idxRef) =>
2941
// For simplification, consuming the entirety of selection index and array
3042
val rvSet = consumeFrom(relValRef.get, assignMap, currentSet)
@@ -43,6 +55,9 @@ object StateAnalysis:
4355
// }
4456
???
4557
case IteratorDcl() => currentSet
58+
// out ports of child designs are not consuming state within the current design
59+
case dcl @ DclOut()
60+
if dcl.getOwnerDesign.isOneLevelBelow(currentBlock.getThisOrOwnerDesign) => currentSet
4661
case dcl: DFVal.Dcl if (dcl.isPortOut || dcl.isVar) && !dcl.isReg && !dcl.isInProcess =>
4762
value.getConnectionTo match
4863
case Some(DFNet.Connection(_, fromVal: DFVal, _)) =>
@@ -58,10 +73,10 @@ object StateAnalysis:
5873
value: DFVal,
5974
assignMap: AssignMap,
6075
currentSet: Set[DFVal]
61-
)(using MemberGetSet): Set[DFVal] =
76+
)(using MemberGetSet, DFBlock): Set[DFVal] =
6277
value.dfType match
6378
case _: DFUnbounded => currentSet
64-
case _ =>
79+
case _ =>
6580
consumeFrom(value, value.dfType.width, 0, assignMap, currentSet)
6681

6782
@tailrec private def assignTo(
@@ -74,8 +89,13 @@ object StateAnalysis:
7489
value match
7590
case DFVal.Alias.AsIs(relValRef = relValRef) =>
7691
assignTo(relValRef.get, relWidth, relBitLow, assignMap)
77-
case DFVal.Alias.ApplyRange(relValRef = relValRef, relBitHigh = rbh, relBitLow = rbl) =>
78-
assignTo(relValRef.get, relWidth, rbl + relBitLow, assignMap)
92+
case applyRange @ DFVal.Alias.ApplyRange(
93+
relValRef = relValRef,
94+
idxHighRef = Int(idxHigh),
95+
idxLowRef = Int(idxLow)
96+
) =>
97+
val elementWidth = applyRange.elementWidth
98+
assignTo(relValRef.get, relWidth, idxLow * elementWidth + relBitLow, assignMap)
7999
case DFVal.Alias.ApplyIdx(relValRef = relValRef, relIdx = idxRef) =>
80100
// for simplification, assigning the entirety of the array
81101
assignTo(relValRef.get, assignMap)
@@ -104,6 +124,7 @@ object StateAnalysis:
104124
currentSet: Set[DFVal],
105125
checkedDomain: DomainType => Boolean
106126
)(using MemberGetSet): (Set[DFVal], AssignMap) =
127+
given DFBlock = currentBlock
107128
remaining match
108129
case (nextBlock: DFBlock) :: rs if nextBlock.getOwnerBlock == currentBlock => // entering child block
109130
val (updatedSet, updatedScopeMap): (Set[DFVal], AssignMap) = nextBlock match
@@ -170,6 +191,8 @@ object StateAnalysis:
170191
getImplicitStateVars(remaining, currentBlock.getOwnerBlock, updatedScopeMap, updatedSet,
171192
checkedDomain)
172193
else (updatedSet, scopeMap)
194+
end match
195+
end getImplicitStateVars
173196

174197
type AssignMap = Map[DFVal, AssignedScope]
175198

compiler/ir/src/main/scala/dfhdl/compiler/ir/DFMember.scala

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ sealed trait DFMember extends Product, Serializable, HasRefCompare[DFMember] der
1717
protected def setMeta(meta: Meta): this.type
1818
protected def setTags(tags: DFTags): this.type
1919
final def getOwner(using MemberGetSet): DFOwner = ownerRef.get match
20-
case o: DFOwner => o
20+
case o: DFOwner => o
2121
case _: DFMember.Empty =>
2222
throw new IllegalArgumentException(s"No owner found for member $this.")
2323
final def getOwnerNamed(using MemberGetSet): DFOwnerNamed = getOwner match
@@ -78,7 +78,7 @@ sealed trait DFMember extends Product, Serializable, HasRefCompare[DFMember] der
7878
): Boolean =
7979
thisMember match
8080
case DFDesignBlock.Top() => false
81-
case _ =>
81+
case _ =>
8282
(thisMember.getOwner, thatOwner) match
8383
case (a, b) if a == b => true
8484
case (od, _) => isInsideOwner(od, thatOwner)
@@ -88,7 +88,7 @@ sealed trait DFMember extends Product, Serializable, HasRefCompare[DFMember] der
8888
final def getOwnerChain(using MemberGetSet): List[DFBlock] =
8989
this match
9090
case d @ DFDesignBlock.Top() => Nil
91-
case _ =>
91+
case _ =>
9292
if (getOwnerBlock.isTop) List(getOwnerBlock)
9393
else getOwnerBlock.getOwnerChain :+ getOwnerBlock
9494
// count the hierarchy distance from inside to outside
@@ -302,7 +302,7 @@ object DFVal:
302302
val relVal = partial.relValRef.get
303303
partial match
304304
case partial: DFVal.Alias.ApplyRange =>
305-
relVal.departial(range.offset(partial.relBitLow))
305+
relVal.departial(range.offset(partial.idxLowRef.getInt))
306306
case partial: DFVal.Alias.ApplyIdx =>
307307
partial.relIdx.get match
308308
case DFVal.Alias.ApplyIdx.ConstIdx(idx) =>
@@ -739,40 +739,60 @@ object DFVal:
739739
case _ => false
740740

741741
final case class ApplyRange(
742+
dfType: DFType,
742743
relValRef: PartialRef,
743-
relBitHigh: Int,
744-
relBitLow: Int,
744+
idxHighRef: IntParamRef,
745+
idxLowRef: IntParamRef,
745746
ownerRef: DFOwner.Ref,
746747
meta: Meta,
747748
tags: DFTags
748749
) extends Partial derives ReadWriter:
750+
def elementWidth(using MemberGetSet): Int = (dfType: @unchecked) match
751+
case DFBits(_) => 1
752+
case DFVector(cellType = cellType) => cellType.width
749753
protected def protIsFullyAnonymous(using MemberGetSet): Boolean =
750754
relValRef.get.isFullyAnonymous
751755
protected def protGetConstData(using MemberGetSet): Option[Any] =
752756
val relVal = relValRef.get
753757
relVal.getConstData.map(relValData =>
754-
selBitRangeData(relValData.asInstanceOf[(BitVector, BitVector)], relBitHigh, relBitLow)
758+
selRangeData(
759+
relVal.dfType,
760+
relValData,
761+
idxHighRef.getInt,
762+
idxLowRef.getInt
763+
)
755764
)
756-
val dfType: DFType = DFBits(relBitHigh - relBitLow + 1)
757765
protected def `prot_=~`(that: DFMember)(using MemberGetSet): Boolean = that match
758766
case that: ApplyRange =>
759-
this.relValRef =~ that.relValRef &&
760-
this.relBitHigh == that.relBitHigh && this.relBitLow == that.relBitLow &&
767+
this.dfType =~ that.dfType && this.relValRef =~ that.relValRef &&
768+
this.idxHighRef =~ that.idxHighRef && this.idxLowRef =~ that.idxLowRef &&
761769
this.meta =~ that.meta && this.tags =~ that.tags
762770
case _ => false
763771
protected[ir] def protIsSimilarTo(that: CanBeExpr)(using MemberGetSet): Boolean =
764772
that match
765773
case that: ApplyRange =>
774+
this.dfType.isSimilarTo(that.dfType) &&
766775
this.relValRef.get.isSimilarTo(that.relValRef.get) &&
767-
this.relBitHigh == that.relBitHigh && this.relBitLow == that.relBitLow
776+
this.idxHighRef.isSimilarTo(that.idxHighRef) && this.idxLowRef.isSimilarTo(
777+
that.idxLowRef
778+
)
768779
case _ => false
769780
protected def setMeta(meta: Meta): this.type = copy(meta = meta).asInstanceOf[this.type]
770781
protected def setTags(tags: DFTags): this.type = copy(tags = tags).asInstanceOf[this.type]
782+
override lazy val getRefs: List[DFRef.TwoWayAny] =
783+
dfType.getRefs ++ List(relValRef) ++ (idxHighRef match
784+
case ref: DFRef.TypeRef => List(ref);
785+
case _ => Nil) ++ (idxLowRef match
786+
case ref: DFRef.TypeRef => List(ref);
787+
case _ => Nil)
771788
def updateDFType(dfType: DFType): this.type = this
772789
def copyWithoutGlobalCtx: this.type = copy().asInstanceOf[this.type]
773790
def copyWithNewRefs(using RefGen): this.type = copy(
791+
dfType = dfType.copyWithNewRefs,
774792
ownerRef = ownerRef.copyAsNewRef,
775-
relValRef = relValRef.copyAsNewRef
793+
relValRef = relValRef.copyAsNewRef,
794+
idxHighRef = idxHighRef.copyAsNewRef,
795+
idxLowRef = idxLowRef.copyAsNewRef
776796
).asInstanceOf[this.type]
777797
end ApplyRange
778798
final case class ApplyIdx(
@@ -1460,9 +1480,9 @@ final case class DomainBlock(
14601480
tags: DFTags
14611481
) extends DFBlock,
14621482
DFDomainOwner derives ReadWriter:
1463-
def flattenMode: dfhdl.hw.flattenMode = meta.annotations.collectFirst {
1464-
case fm: dfhdl.hw.flattenMode => fm
1465-
}.getOrElse(dfhdl.hw.flattenMode.defaultPrefixUnderscore)
1483+
def flattenMode: dfhdl.hw.annotation.flattenMode = meta.annotations.collectFirst {
1484+
case fm: dfhdl.hw.annotation.flattenMode => fm
1485+
}.getOrElse(dfhdl.hw.annotation.flattenMode.defaultPrefixUnderscore)
14661486
protected def `prot_=~`(that: DFMember)(using MemberGetSet): Boolean = that match
14671487
case that: DomainBlock =>
14681488
this.domainType =~ that.domainType &&

0 commit comments

Comments
 (0)