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