@@ -11,6 +11,7 @@ import java.nio.file.Paths
11
11
import dfhdl .backends
12
12
import dfhdl .compiler .stages .verilog .VerilogDialect
13
13
import dfhdl .compiler .stages .vhdl .VHDLDialect
14
+ import dfhdl .hw .constraints
14
15
15
16
object Vivado extends Builder :
16
17
val toolName : String = " Vivado"
@@ -27,7 +28,10 @@ object Vivado extends Builder:
27
28
): CompiledDesign =
28
29
addSourceFiles(
29
30
cd,
30
- List (new VivadoProjectTclConfigPrinter (using cd.stagedDB.getSet).getSourceFile)
31
+ List (
32
+ new VivadoProjectTclConfigPrinter (using cd.stagedDB.getSet).getSourceFile,
33
+ new VivadoProjectConstraintsPrinter (using cd.stagedDB.getSet).getSourceFile
34
+ )
31
35
)
32
36
def build (
33
37
cd : CompiledDesign
@@ -40,14 +44,17 @@ object Vivado extends Builder:
40
44
end Vivado
41
45
42
46
val VivadoProjectTclConfig = SourceType .Tool (" Vivado" , " ProjectTclConfig" )
47
+ val VivadoProjectConstraints = SourceType .Tool (" Vivado" , " ProjectConstraints" )
43
48
44
49
class VivadoProjectTclConfigPrinter (using getSet : MemberGetSet , co : CompilerOptions ):
45
50
val designDB : DB = getSet.designDB
46
51
val topName : String = getSet.topName
47
52
val targetLanguage : String = co.backend match
48
53
case _ : backends.verilog => " Verilog"
49
54
case _ : backends.vhdl => " VHDL"
50
- val part : String = " xc7a100tcsg324-1" // TODO: make it configurable
55
+ val part : String = getSet.designDB.top.dclMeta.annotations.collectFirst {
56
+ case annotation : constraints.device => annotation.name
57
+ }.getOrElse(throw new IllegalArgumentException (" No device annotation found" ))
51
58
val fileType : String = co.backend match
52
59
case backend : backends.verilog => backend.dialect match
53
60
case VerilogDialect .v95 | VerilogDialect .v2001 => " Verilog"
@@ -71,6 +78,7 @@ class VivadoProjectTclConfigPrinter(using getSet: MemberGetSet, co: CompilerOpti
71
78
|set_property target_language $targetLanguage [current_project]
72
79
|add_files -norecurse ${hdlFiles.mkString(" {\n " , " \n " , " \n }" )}
73
80
|set_property file_type { ${fileType}} [get_files *]
81
+ |add_files -fileset constrs_1 -norecurse ./ ${topName}.xdc
74
82
|######################################################################
75
83
|# Suppress warnings
76
84
|######################################################################
@@ -92,3 +100,109 @@ class VivadoProjectTclConfigPrinter(using getSet: MemberGetSet, co: CompilerOpti
92
100
def getSourceFile : SourceFile =
93
101
SourceFile (SourceOrigin .Compiled , VivadoProjectTclConfig , configFileName, contents)
94
102
end VivadoProjectTclConfigPrinter
103
+
104
+ class VivadoProjectConstraintsPrinter (using getSet : MemberGetSet , co : CompilerOptions ):
105
+ val designDB : DB = getSet.designDB
106
+ val topName : String = getSet.topName
107
+ val constraintsFileName : String = s " $topName.xdc "
108
+ val topIOs = designDB.top.members(MemberView .Folded ).collect {
109
+ case dcl @ DclPort () => dcl
110
+ }
111
+
112
+ def xdcDesignConstraints : List [String ] =
113
+ designDB.top.dclMeta.annotations.flatMap {
114
+ case deviceConstraint : constraints.device =>
115
+ deviceConstraint.properties.map {
116
+ case (k, v) => s " set_property $k $v [current_design] "
117
+ }
118
+ case _ => Nil
119
+ }.toList
120
+
121
+ def portPattern (port : DFVal .Dcl , constraint : constraints.SigConstraint ): String =
122
+ val portName = port.getName
123
+ constraint.bitIdx match
124
+ case None => s " $portName[*] "
125
+ case bitIdx => s " $portName[ $bitIdx] "
126
+ end portPattern
127
+
128
+ def xdcIOConstraint (
129
+ port : DFVal .Dcl ,
130
+ portConstraint : constraints.io
131
+ ): String =
132
+ var dict = " "
133
+ def addToDict (key : String , value : Any ): Unit =
134
+ if (dict.nonEmpty)
135
+ dict += " "
136
+ dict += s " $key $value"
137
+
138
+ // Location constraint
139
+ portConstraint.loc.foreach { loc =>
140
+ addToDict(" PACKAGE_PIN" , loc)
141
+ }
142
+
143
+ // IO standard constraint
144
+ portConstraint.standard.foreach { standard =>
145
+ val standardStr = standard match
146
+ case constraints.io.Standard .LVCMOS33 => " LVCMOS33"
147
+ case constraints.io.Standard .LVCMOS25 => " LVCMOS25"
148
+ case constraints.io.Standard .LVCMOS18 => " LVCMOS18"
149
+ addToDict(" IOSTANDARD" , standardStr)
150
+ }
151
+
152
+ // Slew rate constraint
153
+ portConstraint.slewRate.foreach { slewRate =>
154
+ val slewRateStr = slewRate match
155
+ case constraints.io.SlewRate .SLOW => " SLOW"
156
+ case constraints.io.SlewRate .FAST => " FAST"
157
+ addToDict(" SLEW" , slewRateStr)
158
+ }
159
+
160
+ // Drive strength constraint
161
+ portConstraint.driveStrength.foreach { driveStrength =>
162
+ addToDict(" DRIVE" , driveStrength)
163
+ }
164
+
165
+ // Pull mode constraint
166
+ portConstraint.pullMode.foreach { pullMode =>
167
+ val pullModeStr = pullMode match
168
+ case constraints.io.PullMode .UP => " PULLUP"
169
+ case constraints.io.PullMode .DOWN => " PULLDOWN"
170
+ addToDict(" PULLMODE" , pullModeStr)
171
+ }
172
+
173
+ s " set_property -dict { $dict} [get_ports { ${portPattern(port, portConstraint)}}] "
174
+ end xdcIOConstraint
175
+
176
+ def xdcTimingIgnoreConstraint (
177
+ port : DFVal .Dcl ,
178
+ constraint : constraints.timing.ignore
179
+ ): String =
180
+ def helper (dir : String ): String =
181
+ s " set_false_path $dir [get_ports { ${portPattern(port, constraint)}}] "
182
+ (port.modifier.dir: @ unchecked) match
183
+ case DFVal .Modifier .IN => helper(" -from" )
184
+ case DFVal .Modifier .OUT => helper(" -to" )
185
+ // TODO: for INOUT, also check that its actually used in both directions by the design
186
+ case DFVal .Modifier .INOUT => helper(" -from" ) + " \n " + helper(" -to" )
187
+ end xdcTimingIgnoreConstraint
188
+
189
+ def xdcPortConstraints (
190
+ port : DFVal .Dcl
191
+ ): List [String ] =
192
+ port.meta.annotations.collect {
193
+ case constraint : constraints.io => xdcIOConstraint(port, constraint)
194
+ case constraint : constraints.timing.ignore => xdcTimingIgnoreConstraint(port, constraint)
195
+ }
196
+ end xdcPortConstraints
197
+
198
+ def xdcPortConstraints : List [String ] =
199
+ topIOs.view.flatMap(xdcPortConstraints).toList
200
+
201
+ def contents : String =
202
+ s """ | ${xdcDesignConstraints.mkString(" \n " )}
203
+ | ${xdcPortConstraints.mkString(" \n " )}
204
+ | """ .stripMargin
205
+
206
+ def getSourceFile : SourceFile =
207
+ SourceFile (SourceOrigin .Compiled , VivadoProjectConstraints , constraintsFileName, contents)
208
+ end VivadoProjectConstraintsPrinter
0 commit comments