Hog v10.13.0
post-implementation.tcl
Go to the documentation of this file.
1 #!/usr/bin/env tclsh
2 # Copyright 2018-2026 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 Hog describe for $proj_name..."
82 set describe [GetHogDescribe [file normalize ./Top/$group_name/$proj_name] $repo_path]
83 Msg Info "Git describe set to: $describe"
84 
85 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
86 
87 set bin_dir [file normalize "$repo_path/bin"]
88 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
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 Warning "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 Hog describe for $proj_name..."
179 set describe [GetHogDescribe [file normalize ./Top/$group_name/$proj_name] $repo_path]
180 Msg Info "Git describe set to: $describe"
181 
182 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
183 
184 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
185 Msg Info "Creating $dst_dir..."
186 file mkdir $dst_dir
187 
188 #check list files and project properties
189 set confDict [dict create]
190 set full_diff_log 0
191 if {[file exists "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]} {
192  set confDict [ReadConf "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]
193  set full_diff_log [DictGet [DictGet $confDict "hog"] "FULL_DIFF_LOG" 0]
194 }
195 
196 Msg Info "Evaluating differences with last commit..."
197 set found_uncommitted 0
198 set diff [Git diff]
199 set diff_stat [Git "diff --stat"]
200 if {$diff != ""} {
201  set found_uncommitted 1
202  Msg Warning "Found non committed changes:"
203  if {$full_diff_log} {
204  Msg Status "$diff"
205  } else {
206  Msg Status "$diff_stat"
207  }
208  set fp [open "$dst_dir/diff_postimplementation.txt" w+]
209  puts $fp "$diff"
210  close $fp
211 }
212 
213 if {$found_uncommitted == 0} {
214  Msg Info "No uncommitted changes found."
215 }
216 
217 # Reports
218 file mkdir $dst_dir/reports
219 
220 if {[IsXilinx]} {
221  if {[IsVivado]} {
222  report_utilization -hierarchical -hierarchical_percentages -file $dst_dir/reports/hierarchical_utilization.txt
223  }
224 
225  if {[IsISE]} {
226  set reps [glob -nocomplain "$run_dir/*/*{.syr,.srp,.mrp,.map,.twr,.drc,.bgn,_routed.par,_routed_pad.txt,_routed.unroutes}"]
227  } else {
228  set reps [glob -nocomplain "$run_dir/*/*.rpt"]
229  }
230 
231  if {[file exists [lindex $reps 0]]} {
232  file copy -force {*}$reps $dst_dir/reports
233  if {[file exists [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"]]} {
234  set utilization_file [file normalize $dst_dir/utilization.txt]
235  set report_file [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"]
236  if {$group_name != ""} {
237  WriteUtilizationSummary $report_file $utilization_file $group_name/$proj_name "Implementation"
238  } else {
239  WriteUtilizationSummary $report_file $utilization_file $proj_name "Implementation"
240  }
241  }
242  } else {
243  Msg Warning "No reports found in $run_dir subfolders"
244  }
245 
246  # Log files
247  set logs [glob -nocomplain "$run_dir/*/runme.log"]
248  foreach log $logs {
249  set run_name [file tail [file dirname $log]]
250  file copy -force $log $dst_dir/reports/$run_name.log
251  }
252 
253  if {[IsVivado]} {
254  if {[info exists env(HOG_SAVE_DCP)] && ($env(HOG_SAVE_DCP) == 1 || $env(HOG_SAVE_DCP) == 3)} {
255  set dcp_dir [file normalize "$repo_path/DCPs/$group_name/$proj_name/impl_dcp"]
256  file mkdir $dcp_dir
257  set dcps [glob -nocomplain "$run_dir/impl*/*.dcp"]
258  if {[file exists [lindex $dcps 0]]} {
259  file copy -force {*}$dcps $dcp_dir
260  }
261  }
262  }
263 } elseif {[IsDiamond]} {
264  #Logs
265  set logs [glob -nocomplain "$proj_dir/Implementation0/*.log"]
266 
267  if {[file exists [lindex $logs 0]]} {
268  file copy -force {*}$logs $dst_dir/reports
269  } else {
270  Msg Warning "No .log reports found in $proj_dir/Implementation0 subfolders"
271  }
272 
273  # Arearep
274  set areas [glob -nocomplain "$proj_dir/Implementation0/*.arearep"]
275 
276  if {[file exists [lindex $areas 0]]} {
277  file copy -force {*}$areas $dst_dir/reports
278  } else {
279  Msg Warning "No .arearep reports found in $proj_dir/Implementation0 subfolders"
280  }
281 
282  # Timing (TWR)
283  set t_reps [glob -nocomplain "$proj_dir/Implementation0/*.twr"]
284 
285  if {[file exists [lindex $t_reps 0]]} {
286  file copy -force {*}$t_reps $dst_dir/reports
287  } else {
288  Msg Warning "No .twr reports found in $proj_dir/Implementation0 subfolders"
289  }
290 
291  # Mapping Report Files (MRP)
292  set m_reps [glob -nocomplain "$proj_dir/Implementation0/*.mrp"]
293 
294  if {[file exists [lindex $m_reps 0]]} {
295  file copy -force {*}$m_reps $dst_dir/reports
296  } else {
297  Msg Warning "No .mrp reports found in $proj_dir/Implementation0 subfolders"
298  }
299 
300  # P&R Report Files (PAR)
301  set par_reps [glob -nocomplain "$proj_dir/Implementation0/*.par"]
302 
303  if {[file exists [lindex $par_reps 0]]} {
304  file copy -force {*}$par_reps $dst_dir/reports
305  } else {
306  Msg Warning "No .par reports found in $proj_dir/Implementation0 subfolders"
307  }
308 }
309 
310 
311 # Run user post-implementation file
312 set user_post_implementation_file "./Top/$group_name/$proj_name/post-implementation.tcl"
313 if {[file exists $user_post_implementation_file]} {
314  Msg Info "Sourcing user post-implementation file $user_post_implementation_file"
315  source $user_post_implementation_file
316 }
317 
318 cd $old_path
319 Msg Info "All done."