@@ -39,6 +39,12 @@ final case class DB(
39
39
case _ => true
40
40
}
41
41
42
+ // considered to be in build if not in simulation and has a device constraint
43
+ lazy val inBuild : Boolean = ! inSimulation && top.dclMeta.annotations.exists {
44
+ case _ : constraints.DeviceID => true
45
+ case _ => false
46
+ }
47
+
42
48
lazy val portsByName : Map [DFDesignInst , Map [String , DFVal .Dcl ]] =
43
49
members.view
44
50
.collect { case m : DFVal .Dcl if m.isPort => m }
@@ -47,6 +53,10 @@ final case class DB(
47
53
design -> dcls.map(m => m.getRelativeName(design) -> m).toMap
48
54
}.toMap
49
55
56
+ lazy val topIOs : List [DFVal .Dcl ] = designMemberTable(top).collect {
57
+ case dcl : DFVal .Dcl if dcl.isPort => dcl
58
+ }
59
+
50
60
// map of all ports and their by-name selectors
51
61
lazy val portsByNameSelectors : Map [DFVal .Dcl , List [DFVal .PortByNameSelect ]] =
52
62
members.view
@@ -81,7 +91,7 @@ final case class DB(
81
91
origMember.getRefs.foreach {
82
92
case _ : DFRef .Empty =>
83
93
case _ : DFRef .TypeRef if excludeTypeRef =>
84
- case r =>
94
+ case r =>
85
95
tbl.updateWith(
86
96
refTable.getOrElse(
87
97
r,
@@ -275,7 +285,7 @@ final case class DB(
275
285
): (DFConditional .Header , List [DFConditional .Block ]) =
276
286
handled += block
277
287
block.prevBlockOrHeaderRef.get match
278
- case header : DFConditional .Header => (header, block :: chain)
288
+ case header : DFConditional .Header => (header, block :: chain)
279
289
case prevBlock : DFConditional .Block =>
280
290
getChain(prevBlock, block :: chain)
281
291
chainMap + getChain(m, Nil )
@@ -391,14 +401,14 @@ final case class DB(
391
401
val toValOption = (lhsAccess, rhsAccess) match
392
402
case (Write , Read | ReadWrite | Unknown ) => Some (lhsVal)
393
403
case (Read | ReadWrite | Unknown , Write ) => Some (rhsVal)
394
- case (Read , Read ) =>
404
+ case (Read , Read ) =>
395
405
newError(" Unsupported read-to-read connection." )
396
406
None
397
407
case (Write , Write ) =>
398
408
newError(" Unsupported write-to-write connection." )
399
409
None
400
- case (_, Read ) => Some (lhsVal)
401
- case (Read , _) => Some (rhsVal)
410
+ case (_, Read ) => Some (lhsVal)
411
+ case (Read , _) => Some (rhsVal)
402
412
case (Error , _) =>
403
413
newError(s " Unknown access pattern with ${lhsVal.relValString}. " )
404
414
None
@@ -834,25 +844,24 @@ final case class DB(
834
844
|Message: $msg""" .stripMargin
835
845
val ownerDomain = wait.getOwnerDomain
836
846
trigger.getConstData match
837
- case Some (( waitValue : BigDecimal , waitUnit : DFTime . Unit ) ) =>
847
+ case Some (waitTime : TimeNumber ) =>
838
848
// Check if the wait statement is in a domain with a clock rate configuration
839
849
explicitRTDomainCfgMap.get(ownerDomain) match
840
850
case Some (RTDomainCfg .Explicit (_, clkCfg : ClkCfg .Explicit , _)) =>
851
+
841
852
// Get the clock period in picoseconds
842
- val (clockPeriodPs : BigDecimal , desc : String ) = clkCfg.rate match
843
- case (value : BigDecimal , unit : DFTime .Unit ) =>
844
- // Direct period specification
845
- (unit.to_ps(value), s " period ${value}. ${unit}" )
846
- case (value : BigDecimal , unit : DFFreq .Unit ) =>
847
- // Frequency specification - convert to period
848
- (unit.to_ps(value), s " frequency ${value}. ${unit}" )
853
+ val clockPeriodPs = clkCfg.rate.to_ps.value
854
+ val desc = clkCfg.rate match
855
+ case time : TimeNumber => s " period ${time}"
856
+ case freq : FreqNumber => s " frequency ${freq}"
857
+
849
858
// Get wait duration in picoseconds
850
- val waitDurationPs = waitUnit .to_ps(waitValue)
859
+ val waitDurationPs = waitTime .to_ps.value
851
860
852
861
// Check if wait duration is exactly divisible by clock period
853
862
if (waitDurationPs % clockPeriodPs != 0 )
854
863
waitError(
855
- s " Wait duration ${waitValue} . ${waitUnit } is not exactly divisible by the clock $desc. "
864
+ s " Wait duration ${waitTime } is not exactly divisible by the clock $desc. "
856
865
)
857
866
case _ =>
858
867
waitError(
@@ -941,7 +950,7 @@ final case class DB(
941
950
val problemReferences : List [(DFMember , DFMember )] =
942
951
membersNoGlobals.view.drop(1 ).flatMap {
943
952
case _ : PortByNameSelect => None
944
- case m =>
953
+ case m =>
945
954
val isDesignParam = m match
946
955
case _ : DFVal .DesignParam => true
947
956
case _ => false
@@ -995,6 +1004,62 @@ final case class DB(
995
1004
)
996
1005
end directRefCheck
997
1006
1007
+ def portLocationCheck (): Unit =
1008
+ val errors = mutable.ListBuffer .empty[String ]
1009
+ val locationCollisions = mutable.ListBuffer .empty[String ]
1010
+
1011
+ // Collect all location constraints to check for collisions
1012
+ val locationMap = mutable.Map .empty[String , String ] // loc -> portName(idx)
1013
+
1014
+ topIOs.foreach(port =>
1015
+ val bitSet = mutable.BitSet ((0 until port.width)* )
1016
+ port.meta.annotations.foreach {
1017
+ case constraints.IO (bitIdx = None , loc = loc : String ) =>
1018
+ bitSet.clear()
1019
+ locationMap.get(loc).foreach { prevPort =>
1020
+ locationCollisions += s " ${prevPort} and ${port.getFullName} are both assigned to location ` ${loc}` "
1021
+ }
1022
+ locationMap += loc -> port.getFullName
1023
+ if (port.width != 1 )
1024
+ locationCollisions += s " ${port.getFullName} has mutliple bits assigned to location ` ${loc}` "
1025
+ case constraints.IO (bitIdx = bitIdx : Int , loc = loc : String ) =>
1026
+ locationMap.get(loc).foreach { prevPort =>
1027
+ locationCollisions += s " ${prevPort} and ${port.getFullName}( ${bitIdx}) are both assigned to location ` ${loc}` "
1028
+ }
1029
+ locationMap += loc -> s " ${port.getFullName}( ${bitIdx}) "
1030
+ bitSet -= bitIdx
1031
+ case _ =>
1032
+ }
1033
+ if (bitSet.nonEmpty)
1034
+ if (port.width == 1 )
1035
+ errors += s " ${port.getFullName}"
1036
+ else
1037
+ errors += s " ${port.getFullName} with bits ${bitSet.mkString(" , " )}"
1038
+ )
1039
+
1040
+ if (errors.nonEmpty)
1041
+ throw new IllegalArgumentException (
1042
+ s """ |The following top ports are missing location constraints:
1043
+ | ${errors.mkString(" \n " )}
1044
+ |To Fix:
1045
+ |Add a location constraint to the ports by connecting them to a located resource or
1046
+ |by using the `@io` constraint.
1047
+ | """ .stripMargin
1048
+ )
1049
+
1050
+ if (locationCollisions.nonEmpty)
1051
+ throw new IllegalArgumentException (
1052
+ s """ |The following location constraints have collisions:
1053
+ | ${locationCollisions.mkString(" \n " )}
1054
+ |To Fix:
1055
+ |Ensure each location is used by a single port bit.
1056
+ | """ .stripMargin
1057
+ )
1058
+ end portLocationCheck
1059
+
1060
+ def buildTopChecks (): Unit =
1061
+ portLocationCheck()
1062
+
998
1063
def check (): Unit =
999
1064
nameCheck()
1000
1065
connectionTable // causes connectivity checks
@@ -1003,6 +1068,8 @@ final case class DB(
1003
1068
directRefCheck()
1004
1069
circularDerivedDomainsCheck()
1005
1070
waitCheck()
1071
+ if (inBuild)
1072
+ buildTopChecks()
1006
1073
1007
1074
// There can only be a single connection to a value in a given range
1008
1075
// (multiple assignments are possible)
@@ -1056,5 +1123,6 @@ trait MemberGetSet:
1056
1123
def remove [M <: DFMember ](member : M ): M
1057
1124
def setGlobalTag [CT <: DFTag : ClassTag ](tag : CT ): Unit
1058
1125
def getGlobalTag [CT <: DFTag : ClassTag ]: Option [CT ]
1126
+ final lazy val topName : String = designDB.top.dclName
1059
1127
1060
1128
def getSet (using MemberGetSet ): MemberGetSet = summon[MemberGetSet ]
0 commit comments