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