Skip to content

Commit 0ad7a26

Browse files
authored
Merge pull request #258 from DFiantHDL/caching
v0.11.0
2 parents 2565b81 + 9b1fbd1 commit 0ad7a26

File tree

115 files changed

+2047
-968
lines changed

Some content is hidden

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

115 files changed

+2047
-968
lines changed

.scalafmt.conf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = 3.9.4
1+
version = 3.9.7
22
runner.dialect = scala3
33

44
maxColumn = 100
@@ -14,4 +14,5 @@ binPack.literalsInclude = [".*"]
1414
binPack.literalsIncludeSimpleExpr = true
1515
binPack.literalsSingleLine = false
1616

17-
newlines.selectChains = keep
17+
newlines.selectChains = keep
18+
newlines.source = keep

build.sbt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ commands += DFHDLCommands.docExamplesRefUpdate
33

44
// format: off
55
val projectName = "dfhdl"
6-
val compilerVersion = "3.7.0-RC4"
6+
val compilerVersion = "3.7.1"
77

88
inThisBuild(
99
List(
@@ -78,7 +78,8 @@ lazy val plugin = project
7878
lazy val compiler_ir = (project in file("compiler/ir"))
7979
.settings(
8080
name := s"$projectName-compiler-ir",
81-
settings
81+
settings,
82+
libraryDependencies += dependencies.upickle
8283
).dependsOn(internals)
8384

8485
lazy val core = project
@@ -146,17 +147,20 @@ lazy val devices = (project in file("devices"))
146147
lazy val dependencies =
147148
new {
148149
private val scodecV = "1.2.1"
149-
private val munitV = "1.1.0"
150+
private val munitV = "1.1.1"
150151
private val scalafmtV = "3.8.3"
151-
private val airframelogV = "2025.1.10"
152+
private val airframelogV = "2025.1.12"
152153
private val oslibV = "0.9.2"
153154
private val scallopV = "5.2.0"
155+
private val upickleV = "4.1.0"
156+
154157
val scodec = "org.scodec" %% "scodec-bits" % scodecV
155158
val munit = "org.scalameta" %% "munit" % munitV % Test
156159
val scalafmt = ("org.scalameta" %% "scalafmt-dynamic" % scalafmtV).cross(CrossVersion.for3Use2_13)
157160
val airframelog = "org.wvlet.airframe" %% "airframe-log" % airframelogV
158161
val oslib = "com.lihaoyi" %% "os-lib" % oslibV
159162
val scallop = "org.rogach" %% "scallop" % scallopV
163+
val upickle = "com.lihaoyi" %% "upickle" % upickleV
160164
}
161165

162166
lazy val commonDependencies = Seq(

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import DFConditional.DFCaseBlock.Pattern
66
import DFVal.Modifier
77

88
import scala.annotation.tailrec
9-
import scala.reflect.{ClassTag, classTag}
109
import dfhdl.compiler.ir.DFConditional.DFMatchHeader
1110

1211
extension [CB <: DFConditional.Block](cb: CB)(using MemberGetSet)

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

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import DFVal.Modifier
77
import DFVal.Func.Op as FuncOp
88
import DFVal.Alias.History.Op as HistoryOp
99
import scala.annotation.tailrec
10-
import scala.reflect.{ClassTag, classTag}
1110
import DFDesignBlock.InstMode
1211
import scala.util.boundary, boundary.break
1312
import scala.annotation.targetName
13+
import scala.collection.immutable.ListMap
1414

1515
object IteratorDcl:
1616
def unapply(dcl: DFVal.Dcl)(using MemberGetSet): Boolean =
17-
dcl.hasTagOf[DFVal.Dcl.IteratorTag.type]
17+
dcl.hasTagOf[IteratorTag]
1818

1919
object Ident:
2020
def unapply(alias: DFVal.Alias.AsIs)(using MemberGetSet): Option[DFVal] =
21-
if (alias.hasTagOf[DFVal.Alias.IdentTag.type]) Some(alias.relValRef.get)
21+
if (alias.hasTagOf[IdentTag]) Some(alias.relValRef.get)
2222
else None
2323

2424
object StrippedPortByNameSelect:
@@ -49,11 +49,11 @@ object StrippedPortByNameSelect:
4949
//```
5050
// object DesignParam:
5151
// def unapply(alias: DFVal.Alias.AsIs)(using MemberGetSet): Option[DFVal] =
52-
// if (alias.hasTagOf[DFVal.Alias.DesignParamTag.type])
52+
// if (alias.hasTagOf[DFVal.Alias.DesignParamTag])
5353
// val relVal = alias.relValRef.get
5454
// // if (
5555
// // relVal.existsInComposedReadDeps { dep =>
56-
// // dep.hasTagOf[DFVal.Alias.DesignParamTag.type] &&
56+
// // dep.hasTagOf[DFVal.Alias.DesignParamTag] &&
5757
// // dep.isSameOwnerDesignAs(alias)
5858
// // }
5959
// // ) None
@@ -84,7 +84,7 @@ object AsOpaque:
8484

8585
object Bind:
8686
def unapply(alias: DFVal.Alias)(using MemberGetSet): Option[DFVal] =
87-
if (alias.getTagOf[Pattern.Bind.Tag.type].isDefined)
87+
if (alias.getTagOf[BindTag].isDefined)
8888
Some(alias.relValRef.get)
8989
else None
9090

@@ -407,14 +407,32 @@ extension (textOut: TextOut)
407407
.flatMap(_.collectRelMembers(false)).toList
408408

409409
extension (member: DFMember)
410-
def isPublicMember(using MemberGetSet): Boolean =
410+
private def isPublicMember(using MemberGetSet): Boolean =
411411
member match
412412
case DclPort() => true
413413
case _: DFVal.DesignParam => true
414414
case _: DomainBlock => true
415415
case _ => false
416416
end extension
417417

418+
extension (members: List[DFMember])
419+
def filterPublicMembers(using MemberGetSet): List[DFMember] =
420+
def getPublicMembersDeps(member: DFMember): List[DFMember] =
421+
member :: member.getRefs.flatMap { r =>
422+
val publicMemberCandidate = r.get
423+
publicMemberCandidate match
424+
case _: DFMember.Empty => Nil
425+
case dfVal: DFVal.CanBeGlobal if dfVal.isGlobal => Nil
426+
case _ if publicMemberCandidate.isSameOwnerDesignAs(member) =>
427+
getPublicMembersDeps(publicMemberCandidate)
428+
case _ => Nil
429+
}
430+
members.view.filter(_.isPublicMember).flatMap {
431+
case p: DFVal.DesignParam => getPublicMembersDeps(p).reverse
432+
case m => Some(m)
433+
}.toList.distinct
434+
end extension
435+
418436
extension (member: DFMember)
419437
def consumesCycles(using MemberGetSet): Boolean =
420438
member match
@@ -427,3 +445,46 @@ extension (member: DFMember)
427445
cb.members(MemberView.Folded).exists(_.consumesCycles)
428446
case _ => false
429447
end extension
448+
449+
/** An extractor that transforms a DFType using a provided update function.
450+
*
451+
* This extractor works in two phases:
452+
* 1. It first applies the `preCheck` predicate to determine if the type should be transformed
453+
* and to extract a helper object of type H.
454+
* 2. Then it recursively processes any nested types (for structs, vectors, opaques).
455+
* 3. Finally, it applies the `updateFunc` to either the original type or the recursively
456+
* processed type, along with the helper object.
457+
*
458+
* @tparam H
459+
* The type of the helper object produced by preCheck and consumed by updateFunc
460+
*/
461+
class ComposedDFTypeReplacement[H](
462+
preCheck: DFType => Option[H],
463+
updateFunc: PartialFunction[(DFType, H), DFType]
464+
)(using
465+
MemberGetSet
466+
):
467+
Extractor =>
468+
def unapply(dfType: DFType): Option[DFType] =
469+
val composed = dfType match
470+
case dt: DFStruct =>
471+
val updatedMap = ListMap.from(dt.fieldMap.view.collect { case (name, Extractor(dfType)) =>
472+
(name, dfType)
473+
})
474+
if (updatedMap.nonEmpty) Some(dt.copy(fieldMap = updatedMap))
475+
else None
476+
case dt: DFOpaque =>
477+
dt.actualType match
478+
case Extractor(dfType) => Some(dt.copy(actualType = dfType))
479+
case _ => None
480+
case dt: DFVector =>
481+
dt.cellType match
482+
case Extractor(dfType) => Some(dt.copy(cellType = dfType))
483+
case _ => None
484+
case _ => None
485+
// if the original type matches the precheck, the extractor will always return some updated value
486+
preCheck(dfType) match
487+
case Some(helper) => Some(updateFunc(composed.getOrElse(dfType), helper))
488+
case None => composed
489+
end unapply
490+
end ComposedDFTypeReplacement

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

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dfhdl.compiler.ir
2-
2+
import dfhdl.internals.StableEnum
3+
import upickle.default.*
34
opaque type ConfigN[T] = T | None.type
45
object ConfigN:
56
given [T]: Conversion[None.type, ConfigN[T]] with
@@ -10,10 +11,22 @@ object ConfigN:
1011
given [T]: CanEqual[ConfigN[T], None.type] = CanEqual.derived
1112
given [T]: CanEqual[None.type, ConfigN[T]] = CanEqual.derived
1213
given [L, R]: CanEqual[ConfigN[L], ConfigN[R]] = CanEqual.derived
14+
given [T](using ReadWriter[T]): ReadWriter[ConfigN[T]] = readwriter[ujson.Value].bimap(
15+
value =>
16+
(value: @unchecked) match
17+
case None => ujson.Null
18+
case value: T => writeJs(value)
19+
,
20+
json =>
21+
json match
22+
case ujson.Null => None
23+
case value => read[T](value)
24+
)
25+
end ConfigN
1326

1427
/** Sets the policy for inclusing the clock or reset signals when they are not needed
1528
*/
16-
enum ClkRstInclusionPolicy derives CanEqual:
29+
enum ClkRstInclusionPolicy extends StableEnum derives CanEqual, ReadWriter:
1730
/** Don't include if not needed
1831
*/
1932
case AsNeeded
@@ -24,44 +37,42 @@ enum ClkRstInclusionPolicy derives CanEqual:
2437

2538
type ClkCfg = ConfigN[ClkCfg.Explicit]
2639
object ClkCfg:
27-
enum Edge derives CanEqual:
40+
enum Edge extends StableEnum derives CanEqual, ReadWriter:
2841
case Rising, Falling
2942

43+
type RateData = (BigDecimal, DFFreq.Unit | DFTime.Unit)
44+
given ReadWriter[DFFreq.Unit | DFTime.Unit] =
45+
ReadWriter.merge(summon[ReadWriter[DFTime.Unit]], summon[ReadWriter[DFFreq.Unit]])
46+
3047
final case class Explicit(
3148
edge: Edge,
32-
rate: DFVal,
49+
rate: RateData,
3350
portName: String,
3451
inclusionPolicy: ClkRstInclusionPolicy
35-
) extends HasRefCompare[Explicit] derives CanEqual:
36-
override protected def prot_=~(that: Explicit)(using MemberGetSet): Boolean =
37-
this.edge == that.edge && this.rate =~ that.rate && this.portName == that.portName &&
38-
this.inclusionPolicy == that.inclusionPolicy
39-
lazy val getRefs: List[DFRef.TwoWayAny] = rate.getRefs
40-
def copyWithNewRefs: this.type = copy(rate = rate.copyWithNewRefs).asInstanceOf[this.type]
52+
) derives CanEqual,
53+
ReadWriter
4154
end ClkCfg
4255

4356
type RstCfg = ConfigN[RstCfg.Explicit]
4457
object RstCfg:
45-
enum Mode derives CanEqual:
58+
enum Mode extends StableEnum derives CanEqual, ReadWriter:
4659
case Async, Sync
47-
enum Active derives CanEqual:
60+
enum Active extends StableEnum derives CanEqual, ReadWriter:
4861
case Low, High
4962

5063
final case class Explicit(
5164
mode: Mode,
5265
active: Active,
5366
portName: String,
5467
inclusionPolicy: ClkRstInclusionPolicy
55-
) derives CanEqual
68+
) derives CanEqual,
69+
ReadWriter
5670
end RstCfg
5771

58-
enum RTDomainCfg extends HasRefCompare[RTDomainCfg] derives CanEqual:
72+
enum RTDomainCfg extends HasRefCompare[RTDomainCfg], StableEnum derives CanEqual, ReadWriter:
5973
case Derived
6074
case Related(relatedDomainRef: RTDomainCfg.RelatedDomainRef) extends RTDomainCfg
61-
case Explicit(name: String, clkCfg: ClkCfg, rstCfg: RstCfg)
62-
extends RTDomainCfg,
63-
NamedGlobal,
64-
DFTagOf[DFDesignBlock]
75+
case Explicit(name: String, clkCfg: ClkCfg, rstCfg: RstCfg) extends RTDomainCfg
6576

6677
def isDerivedNoRst: Boolean = this match
6778
case cfg: Explicit if cfg.name.endsWith(".norst") => true
@@ -79,19 +90,16 @@ enum RTDomainCfg extends HasRefCompare[RTDomainCfg] derives CanEqual:
7990
Explicit(thisName, thisClkCfg: ClkCfg.Explicit, thisRstCfg),
8091
Explicit(thatName, thatClkCfg: ClkCfg.Explicit, thatRstCfg)
8192
) =>
82-
thisName == thatName && thisClkCfg =~ thatClkCfg && thisRstCfg == thatRstCfg
93+
thisName == thatName && thisClkCfg == thatClkCfg && thisRstCfg == thatRstCfg
8394
case _ => this == that
8495

8596
lazy val getRefs: List[DFRef.TwoWayAny] = this match
86-
case Related(relatedDomainRef) => List(relatedDomainRef)
87-
case Explicit(_, clkCfg: ClkCfg.Explicit, rstCfg) => clkCfg.getRefs
88-
case _ => Nil
97+
case Related(relatedDomainRef) => List(relatedDomainRef)
98+
case _ => Nil
8999

90-
def copyWithNewRefs: this.type = this match
100+
def copyWithNewRefs(using RefGen): this.type = this match
91101
case Related(relatedDomainRef) => Related(relatedDomainRef.copyAsNewRef).asInstanceOf[this.type]
92-
case Explicit(name, clkCfg: ClkCfg.Explicit, rstCfg) =>
93-
Explicit(name, clkCfg.copyWithNewRefs, rstCfg).asInstanceOf[this.type]
94-
case _ => this
102+
case _ => this
95103
end RTDomainCfg
96104

97105
object RTDomainCfg:

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

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import scala.collection.immutable.{ListMap, ListSet, BitSet}
77
import dfhdl.internals.*
88
import dfhdl.compiler.printing.{Printer, DefaultPrinter}
99
import DFDesignBlock.InstMode
10+
import upickle.default.*
1011

1112
final case class DB(
1213
members: List[DFMember],
1314
refTable: Map[DFRefAny, DFMember],
14-
globalTags: Map[(Any, ClassTag[?]), DFTag],
15+
globalTags: DFTags,
1516
srcFiles: List[SourceFile]
16-
):
17+
) derives CanEqual, ReadWriter:
1718
private val self = this
1819
given getSet: MemberGetSet with
1920
val designDB: DB = self
@@ -26,12 +27,10 @@ final case class DB(
2627
newMemberFunc(originalMember)
2728
def replace[M <: DFMember](originalMember: M)(newMember: M): M = newMember
2829
def remove[M <: DFMember](member: M): M = member
29-
def setGlobalTag[CT <: DFTag: ClassTag](
30-
taggedElement: Any,
31-
tag: CT
32-
): Unit = {}
33-
def getGlobalTag[CT <: DFTag: ClassTag](taggedElement: Any): Option[CT] =
34-
globalTags.get((taggedElement, classTag[CT])).asInstanceOf[Option[CT]]
30+
def setGlobalTag[CT <: DFTag: ClassTag](tag: CT): Unit = throw new Exception(
31+
"Cannot set global tag on immutable DB"
32+
)
33+
def getGlobalTag[CT <: DFTag: ClassTag]: Option[CT] = globalTags.getTagOf[CT]
3534
end getSet
3635

3736
// considered to be in simulation if the top design has no ports
@@ -725,7 +724,7 @@ final case class DB(
725724
private def getExplicitCfg: RTDomainCfg.Explicit =
726725
domainOwner.domainType match
727726
case DomainType.RT(explicitCfg: RTDomainCfg.Explicit) => explicitCfg
728-
case _ => top.getTagOf[RTDomainCfg.Explicit].get
727+
case _ => globalTags.getTagOf[DefaultRTDomainCfgTag].get.cfg
729728
private def usesClkRst: (Boolean, Boolean) = domainOwner match
730729
case design: DFDesignBlock =>
731730
designUsesClkRst.getOrElseUpdate(
@@ -835,16 +834,16 @@ final case class DB(
835834
|Message: $msg""".stripMargin
836835
val ownerDomain = wait.getOwnerDomain
837836
trigger.getConstData match
838-
case Some((waitValue: BigDecimal, waitUnit: DFPhysical.Unit.Time.Scale)) =>
837+
case Some((waitValue: BigDecimal, waitUnit: DFTime.Unit)) =>
839838
// Check if the wait statement is in a domain with a clock rate configuration
840839
explicitRTDomainCfgMap.get(ownerDomain) match
841840
case Some(RTDomainCfg.Explicit(_, clkCfg: ClkCfg.Explicit, _)) =>
842841
// Get the clock period in picoseconds
843-
val (clockPeriodPs: BigDecimal, desc: String) = clkCfg.rate.getConstData.get match
844-
case (value: BigDecimal, unit: DFPhysical.Unit.Time.Scale) =>
842+
val (clockPeriodPs: BigDecimal, desc: String) = clkCfg.rate match
843+
case (value: BigDecimal, unit: DFTime.Unit) =>
845844
// Direct period specification
846845
(unit.to_ps(value), s"period ${value}.${unit}")
847-
case (value: BigDecimal, unit: DFPhysical.Unit.Freq.Scale) =>
846+
case (value: BigDecimal, unit: DFFreq.Unit) =>
848847
// Frequency specification - convert to period
849848
(unit.to_ps(value), s"frequency ${value}.${unit}")
850849
// Get wait duration in picoseconds
@@ -1038,8 +1037,11 @@ final case class DB(
10381037

10391038
end DB
10401039

1041-
//object DB:
1042-
//end DB
1040+
object DB:
1041+
extension (db: DB)
1042+
def toJsonString: String = write(db)
1043+
def fromJsonString(json: String): DB = read[DB](json)
1044+
end DB
10431045

10441046
enum MemberView derives CanEqual:
10451047
case Folded, Flattened
@@ -1052,7 +1054,7 @@ trait MemberGetSet:
10521054
def set[M <: DFMember](originalMember: M)(newMemberFunc: M => M): M
10531055
def replace[M <: DFMember](originalMember: M)(newMember: M): M
10541056
def remove[M <: DFMember](member: M): M
1055-
def setGlobalTag[CT <: DFTag: ClassTag](taggedElement: Any, tag: CT): Unit
1056-
def getGlobalTag[CT <: DFTag: ClassTag](taggedElement: Any): Option[CT]
1057+
def setGlobalTag[CT <: DFTag: ClassTag](tag: CT): Unit
1058+
def getGlobalTag[CT <: DFTag: ClassTag]: Option[CT]
10571059

10581060
def getSet(using MemberGetSet): MemberGetSet = summon[MemberGetSet]

0 commit comments

Comments
 (0)