Hog Hog2025.2-3
post-implementation.tcl
Go to the documentation of this file.
1 #!/usr/bin/env tclsh
2 # Copyright 2018-2025 The University of Birmingham
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 # @file
17 # The post implementation script embeds the git SHA of the current commit into the binary file of the project.
18 # In Vivado this is done using the USERID and USR_ACCESS variables. In Quartus this is done with the STRATIX_JTAG_USER_CODE variable.
19 #
20 # The USERID is always set to the commit, while the USR_ACCESS and STRATIX_JTAG_USER_CODE are set only
21 # if Hog can guarantee the reproducibility of the firmware workflow:
22 #
23 # - The firmware repostory must be clean (no uncommitted modification)
24 # - The Multithread option must be disabled
25 # This script is automatically integrated into the Vivado/Quartus workflow by the Create Project script.
26 
27 ##nagelfar variable quartus
28 
29 set old_path [pwd]
30 set tcl_path [file normalize "[file dirname [info script]]/.."]
31 source $tcl_path/hog.tcl
32 
33 
34 # Import tcllib
35 if {[IsSynplify] || [IsDiamond]} {
36  if {[info exists env(HOG_TCLLIB_PATH)]} {
37  lappend auto_path $env(HOG_TCLLIB_PATH)
38  } else {
39  puts "ERROR: To run Hog with Microsemi Libero SoC, you need to define the HOG_TCLLIB_PATH variable."
40  return
41  }
42 }
43 
44 if {[IsXilinx]} {
45  # Vivado + planAhead
46  if {[IsISE]} {
47  set proj_dir [get_property DIRECTORY [current_project]]
48  set proj_file $proj_dir/[file tail $proj_dir].prr
49  set work_path [get_property DIRECTORY [get_runs impl_1]]
50  } else {
51  set proj_file [get_property parent.project_path [current_project]]
52  set work_path $old_path
53  }
54  set proj_dir [file normalize [file dirname $proj_file]]
55  set proj_name [file rootname [file tail $proj_file]]
56  set run_dir [file normalize "$work_path/.."]
57  set top_name [get_property top [current_fileset]]
58 } elseif {[IsQuartus]} {
59  # Quartus
60  set proj_name [lindex $quartus(args) 1]
61  set proj_dir $old_path
62 } elseif {[IsDiamond]} {
63  set proj_dir [file normalize "[pwd]/.."]
64  set proj_name [file tail $proj_dir]
65  set project $proj_name
66 } else {
67  #Tclssh
68  set proj_file $old_path/[file tail $old_path].xpr
69  Msg CriticalWarning "You seem to be running locally on tclsh, so this is a debug message. \
70  The project file will be set to $proj_file and was derived from the path you launched this script from: $old_path. \
71  If you want this script to work properly in debug mode, please launch it from the top folder of one project, \
72  for example Repo/Projects/fpga1/ or Repo/Top/fpga1/"
73 }
74 
75 set group_name [GetGroupName $proj_dir "$tcl_path/../.."]
76 
77 # Go to repository pathcd $old_pathcd $old_path
78 cd $tcl_path/../../
79 set repo_path "$tcl_path/../.."
80 
81 Msg Info "Evaluating Git sha for $proj_name..."
82 lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] sha
83 
84 set describe [GetHogDescribe $sha $repo_path]
85 Msg Info "Git describe set to: $describe"
86 
87 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
88 
89 set bin_dir [file normalize "$repo_path/bin"]
90 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
91 
92 
93 Msg Info "Evaluating last git SHA in which $proj_name was modified..."
94 set commit "0000000"
95 
96 
97 #check if diff_presynthesis.txt is not empty (problem with list files or conf files)
98 if {[file exists $dst_dir/diff_presynthesis.txt]} {
99  set fp [open "$dst_dir/diff_presynthesis.txt" r]
100  set file_data [read $fp]
101  close $fp
102  if {$file_data != ""} {
103  Msg CriticalWarning "Git working directory [pwd] not clean, git commit hash be set to 0."
104  set commit_usr "0000000"
105  set commit "0000000"
106  } else {
107  lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] commit version
108  }
109 } else {
110  lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] commit version
111 }
112 
113 #number of threads
114 set maxThreads [GetMaxThreads [file normalize ./Top/$group_name/$proj_name]]
115 if {$maxThreads != 1} {
116  Msg CriticalWarning "Multithreading enabled. Number of threads: $maxThreads"
117  set commit_usr "0000000"
118 } else {
119  set commit_usr $commit
120 }
121 
122 #check if diff_list_and_conf.txt is not empty (problem with list files or conf files)
123 if {[file exists $dst_dir/diff_list_and_conf.txt]} {
124  set fp [open "$dst_dir/diff_list_and_conf.txt" r]
125  set file_data [read $fp]
126  close $fp
127  if {$file_data != ""} {
128  Msg CriticalWarning "List files and project properties not clean, git commit hash be set to 0."
129  set commit_usr "0000000"
130  set commit "0000000"
131  }
132 }
133 
134 Msg Info "The git SHA value $commit will be embedded in the binary file."
135 
136 # Set bitstream embedded variables
137 if {[IsXilinx]} {
138  if {[IsISE]} {
139  # get the existing "more options" so that we can append to them when adding the userid
140  set props [get_property "STEPS.BITGEN.ARGS.MORE OPTIONS" [get_runs impl_1]]
141  # need to trim off the curly braces that were used in creating a dictionary
142  regsub -all {\{|\}} $props "" props
143  set PART [get_property part [current_project]]
144  # only some part families have both usr_access and userid
145  if {[string first "xc5v" $PART] != -1 || [string first "xc6v" $PART] != -1 || [string first "xc7" $PART] != -1} {
146  set props "$props -g usr_access:0x[format %08X 0x$commit] -g userid:0x[format %08X 0x$commit_usr]"
147  } else {
148  set props "$props -g userid:0x[format %08X 0x$commit_usr]"
149  }
150  set_property -name {steps.bitgen.args.More Options} -value $props -objects [get_runs impl_1]
151  } else {
152  if {[IsVersal [get_property PART [current_design]]]} {
153  Msg Info "This design uses a Versal chip, USERID does not exist."
154  } else {
155  set_property BITSTREAM.CONFIG.USERID $commit [current_design]
156  }
157  set_property BITSTREAM.CONFIG.USR_ACCESS $commit_usr [current_design]
158  }
159 } elseif {[IsQuartus]} {
160  cd $proj_dir
161  project_open $proj_name -current_revision
162  cd $old_path
163  set_global_assignment -name USE_CHECKSUM_AS_USERCODE OFF
164  set_global_assignment -name STRATIX_JTAG_USER_CODE $commit
165  project_close
166 } else {
167  # Tclsh
168 }
169 
170 set user_post_implementation_file "./Top/$group_name/$proj_name/post-implementation.tcl"
171 if {[file exists $user_post_implementation_file]} {
172  Msg Info "Sourcing user post_implementation file $user_post_implementation_file"
173  source $user_post_implementation_file
174 }
175 
176 
177 # Go to repository path
178 cd $tcl_path/../../
179 
180 Msg Info "Evaluating Git sha for $proj_name..."
181 lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] sha
182 
183 set describe [GetHogDescribe $sha $repo_path]
184 Msg Info "Git describe set to: $describe"
185 
186 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
187 
188 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
189 Msg Info "Creating $dst_dir..."
190 file mkdir $dst_dir
191 
192 #check list files and project properties
193 set confDict [dict create]
194 set full_diff_log 0
195 if {[file exists "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]} {
196  set confDict [ReadConf "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]
197  set full_diff_log [DictGet [DictGet $confDict "hog"] "FULL_DIFF_LOG" 0]
198 }
199 
200 Msg Info "Evaluating differences with last commit..."
201 set found_uncommitted 0
202 set diff [Git diff]
203 set diff_stat [Git "diff --stat"]
204 if {$diff != ""} {
205  set found_uncommitted 1
206  Msg Warning "Found non committed changes:"
207  if {$full_diff_log} {
208  Msg Status "$diff"
209  } else {
210  Msg Status "$diff_stat"
211  }
212  set fp [open "$dst_dir/diff_postimplementation.txt" w+]
213  puts $fp "$diff"
214  close $fp
215 }
216 
217 if {$found_uncommitted == 0} {
218  Msg Info "No uncommitted changes found."
219 }
220 
221 # Reports
222 file mkdir $dst_dir/reports
223 
224 if {[IsXilinx]} {
225  if {[IsVivado]} {
226  report_utilization -hierarchical -hierarchical_percentages -file $dst_dir/reports/hierarchical_utilization.txt
227  }
228 
229  if {[IsISE]} {
230  set reps [glob -nocomplain "$run_dir/*/*{.syr,.srp,.mrp,.map,.twr,.drc,.bgn,_routed.par,_routed_pad.txt,_routed.unroutes}"]
231  } else {
232  set reps [glob -nocomplain "$run_dir/*/*.rpt"]
233  }
234 
235  if {[file exists [lindex $reps 0]]} {
236  file copy -force {*}$reps $dst_dir/reports
237  if {[file exists [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"]]} {
238  set utilization_file [file normalize $dst_dir/utilization.txt]
239  set report_file [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"]
240  if {$group_name != ""} {
241  WriteUtilizationSummary $report_file $utilization_file $group_name/$proj_name "Implementation"
242  } else {
243  WriteUtilizationSummary $report_file $utilization_file $proj_name "Implementation"
244  }
245  }
246  } else {
247  Msg Warning "No reports found in $run_dir subfolders"
248  }
249 
250  # Log files
251  set logs [glob -nocomplain "$run_dir/*/runme.log"]
252  foreach log $logs {
253  set run_name [file tail [file dirname $log]]
254  file copy -force $log $dst_dir/reports/$run_name.log
255  }
256 
257  if {[IsVivado]} {
258  if {[info exists env(HOG_SAVE_DCP)] && ($env(HOG_SAVE_DCP) == 1 || $env(HOG_SAVE_DCP) == 3)} {
259  set dcp_dir [file normalize "$repo_path/DCPs/$group_name/$proj_name/impl_dcp"]
260  file mkdir $dcp_dir
261  set dcps [glob -nocomplain "$run_dir/impl*/*.dcp"]
262  if {[file exists [lindex $dcps 0]]} {
263  file copy -force {*}$dcps $dcp_dir
264  }
265  }
266  }
267 } elseif {[IsDiamond]} {
268  #Logs
269  set logs [glob -nocomplain "$proj_dir/Implementation0/*.log"]
270 
271  if {[file exists [lindex $logs 0]]} {
272  file copy -force {*}$logs $dst_dir/reports
273  } else {
274  Msg Warning "No .log reports found in $proj_dir/Implementation0 subfolders"
275  }
276 
277  # Arearep
278  set areas [glob -nocomplain "$proj_dir/Implementation0/*.arearep"]
279 
280  if {[file exists [lindex $areas 0]]} {
281  file copy -force {*}$areas $dst_dir/reports
282  } else {
283  Msg Warning "No .arearep reports found in $proj_dir/Implementation0 subfolders"
284  }
285 
286  # Timing (TWR)
287  set t_reps [glob -nocomplain "$proj_dir/Implementation0/*.twr"]
288 
289  if {[file exists [lindex $t_reps 0]]} {
290  file copy -force {*}$t_reps $dst_dir/reports
291  } else {
292  Msg Warning "No .twr reports found in $proj_dir/Implementation0 subfolders"
293  }
294 
295  # Mapping Report Files (MRP)
296  set m_reps [glob -nocomplain "$proj_dir/Implementation0/*.mrp"]
297 
298  if {[file exists [lindex $m_reps 0]]} {
299  file copy -force {*}$m_reps $dst_dir/reports
300  } else {
301  Msg Warning "No .mrp reports found in $proj_dir/Implementation0 subfolders"
302  }
303 
304  # P&R Report Files (PAR)
305  set par_reps [glob -nocomplain "$proj_dir/Implementation0/*.par"]
306 
307  if {[file exists [lindex $par_reps 0]]} {
308  file copy -force {*}$par_reps $dst_dir/reports
309  } else {
310  Msg Warning "No .par reports found in $proj_dir/Implementation0 subfolders"
311  }
312 }
313 
314 
315 # Run user post-implementation file
316 set user_post_implementation_file "./Top/$group_name/$proj_name/post-implementation.tcl"
317 if {[file exists $user_post_implementation_file]} {
318  Msg Info "Sourcing user post-implementation file $user_post_implementation_file"
319  source $user_post_implementation_file
320 }
321 
322 cd $old_path
323 Msg Info "All done."