Hog Hog2024.2-4
post-implementation.tcl
Go to the documentation of this file.
1 #!/usr/bin/env tclsh
2 # Copyright 2018-2024 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 # Go to repository pathcd $old_pathcd $old_path
33 cd $tcl_path/../../
34 set repo_path "$tcl_path/../.."
35 
36 if {[IsXilinx]} {
37  # Vivado + planAhead
38  if {[IsISE]} {
39  set proj_file [get_property DIRECTORY [current_project]]
40  set work_path [get_property DIRECTORY [get_runs impl_1]]
41  } else {
42  set proj_file [get_property parent.project_path [current_project]]
43  set work_path $old_path
44  }
45  set proj_dir [file normalize [file dirname $proj_file]]
46  set proj_name [file rootname [file tail $proj_file]]
47  set run_dir [file normalize "$work_path/.."]
48  set top_name [get_property top [current_fileset]]
49 
50 } elseif {[IsQuartus]} {
51  # Quartus
52  set proj_name [lindex $quartus(args) 1]
53  set proj_dir $old_path
54 } else {
55  #Tclssh
56  set proj_file $old_path/[file tail $old_path].xpr
57  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/"
58 }
59 
60 set group_name [GetGroupName $proj_dir "$tcl_path/../.."]
61 Msg Info "Evaluating Git sha for $proj_name..."
62 lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] sha
63 
64 set describe [GetHogDescribe $sha $repo_path]
65 Msg Info "Git describe set to: $describe"
66 
67 set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
68 
69 set bin_dir [file normalize "$repo_path/bin"]
70 set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
71 
72 
73 
74 Msg Info "Evaluating last git SHA in which $proj_name was modified..."
75 set commit "0000000"
76 
77 
78 #check if diff_presynthesis.txt is not empty (problem with list files or conf files)
79 if {[file exists $dst_dir/diff_presynthesis.txt]} {
80  set fp [open "$dst_dir/diff_presynthesis.txt" r]
81  set file_data [read $fp]
82  close $fp
83  if {$file_data != ""} {
84  Msg CriticalWarning "Git working directory [pwd] not clean, git commit hash be set to 0."
85  set commit_usr "0000000"
86  set commit "0000000"
87  } else {
88  lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path ] commit version
89  }
90 } else {
91  lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path ] commit version
92 }
93 
94 #number of threads
95 set maxThreads [GetMaxThreads [file normalize ./Top/$group_name/$proj_name]]
96 if {$maxThreads != 1} {
97  Msg CriticalWarning "Multithreading enabled. Number of threads: $maxThreads"
98  set commit_usr "0000000"
99 } else {
100  set commit_usr $commit
101 }
102 
103 #check if diff_list_and_conf.txt is not empty (problem with list files or conf files)
104 if {[file exists $dst_dir/diff_list_and_conf.txt]} {
105  set fp [open "$dst_dir/diff_list_and_conf.txt" r]
106  set file_data [read $fp]
107  close $fp
108  if {$file_data != ""} {
109  Msg CriticalWarning "List files and project properties not clean, git commit hash be set to 0."
110  set commit_usr "0000000"
111  set commit "0000000"
112  }
113 }
114 
115 Msg Info "The git SHA value $commit will be embedded in the binary file."
116 
117 # Set bitstream embedded variables
118 if {[IsXilinx]} {
119  if {[IsISE]} {
120  # get the existing "more options" so that we can append to them when adding the userid
121  set props [get_property "STEPS.BITGEN.ARGS.MORE OPTIONS" [get_runs impl_1]]
122  # need to trim off the curly braces that were used in creating a dictionary
123  regsub -all {\{|\}} $props "" props
124  set PART [get_property part [current_project]]
125  # only some part families have both usr_access and userid
126  if {[string first "xc5v" $PART] != -1 || [string first "xc6v" $PART] != -1 || [string first "xc7" $PART] != -1} {
127  set props "$props -g usr_access:0x[format %08X 0x$commit] -g userid:0x[format %08X 0x$commit_usr]"
128  } else {
129  set props "$props -g userid:0x[format %08X 0x$commit_usr]"
130  }
131  set_property -name {steps.bitgen.args.More Options} -value $props -objects [get_runs impl_1]
132  } else {
133  if {[IsVersal [get_property PART [current_design]]]} {
134  Msg Info "This design uses a Versal chip, USERID does not exist."
135  } else {
136  set_property BITSTREAM.CONFIG.USERID $commit [current_design]
137  }
138  set_property BITSTREAM.CONFIG.USR_ACCESS $commit_usr [current_design]
139  }
140 } elseif {[IsQuartus]} {
141  cd $proj_dir
142  project_open $proj_name -current_revision
143  cd $old_path
144  set_global_assignment -name USE_CHECKSUM_AS_USERCODE OFF
145  set_global_assignment -name STRATIX_JTAG_USER_CODE $commit
146  project_close
147 } else {
148  # Tclsh
149 }
150 
151 set user_post_implementation_file "./Top/$group_name/$proj_name/post-implementation.tcl"
152 if {[file exists $user_post_implementation_file]} {
153  Msg Info "Sourcing user post_implementation file $user_post_implementation_file"
154  source $user_post_implementation_file
155 }
156 
157 # Vivado
158 if {[IsXilinx]} {
159  # Go to repository path
160  cd $tcl_path/../../
161 
162  Msg Info "Evaluating Git sha for $proj_name..."
163  lassign [GetRepoVersions [file normalize ./Top/$group_name/$proj_name] $repo_path] sha
164 
165  set describe [GetHogDescribe $sha $repo_path]
166  Msg Info "Git describe set to: $describe"
167 
168  set ts [clock format [clock seconds] -format {%Y-%m-%d-%H-%M}]
169 
170  set dst_dir [file normalize "$bin_dir/$group_name/$proj_name\-$describe"]
171  Msg Info "Creating $dst_dir..."
172  file mkdir $dst_dir
173 
174  #check list files and project properties
175  set confDict [dict create]
176  set full_diff_log 0
177  if {[file exists "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]} {
178  set confDict [ReadConf "$tcl_path/../../Top/$group_name/$proj_name/hog.conf"]
179  set full_diff_log [DictGet [DictGet $confDict "hog"] "FULL_DIFF_LOG" 0]
180  }
181 
182  Msg Info "Evaluating differences with last commit..."
183  set found_uncommitted 0
184  set diff [Git diff]
185  set diff_stat [Git "diff --stat"]
186  if {$diff != ""} {
187  set found_uncommitted 1
188  Msg Warning "Found non committed changes:"
189  if {$full_diff_log} {
190  Msg Status "$diff"
191  } else {
192  Msg Status "$diff_stat"
193  }
194  set fp [open "$dst_dir/diff_postimplementation.txt" w+]
195  puts $fp "$diff"
196  close $fp
197  }
198 
199  if {$found_uncommitted == 0} {
200  Msg Info "No uncommitted changes found."
201  }
202 
203  # Reports
204  file mkdir $dst_dir/reports
205  if {[IsVivado]} {
206  report_utilization -hierarchical -hierarchical_percentages -file $dst_dir/reports/hierarchical_utilization.txt
207  }
208 
209 
210  if {[IsISE]} {
211  set reps [glob -nocomplain "$run_dir/*/*{.syr,.srp,.mrp,.map,.twr,.drc,.bgn,_routed.par,_routed_pad.txt,_routed.unroutes}"]
212  } else {
213  set reps [glob -nocomplain "$run_dir/*/*.rpt"]
214  }
215  if {[file exists [lindex $reps 0]]} {
216  file copy -force {*}$reps $dst_dir/reports
217  if {[file exists [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"] ]} {
218  set utilization_file [file normalize $dst_dir/utilization.txt]
219  set report_file [glob -nocomplain "$dst_dir/reports/${top_name}_utilization_placed.rpt"]
220  if {$group_name != ""} {
221  WriteUtilizationSummary $report_file $utilization_file $group_name/$proj_name "Implementation"
222  } else {
223  WriteUtilizationSummary $report_file $utilization_file $proj_name "Implementation"
224  }
225  }
226  } else {
227  Msg Warning "No reports found in $run_dir subfolders"
228  }
229 
230  # Log files
231  set logs [glob -nocomplain "$run_dir/*/runme.log"]
232  foreach log $logs {
233  set run_name [file tail [file dirname $log]]
234  file copy -force $log $dst_dir/reports/$run_name.log
235  }
236 
237 }
238 
239 # Run user post-implementation file
240 set user_post_implementation_file "./Top/$group_name/$proj_name/post-implementation.tcl"
241 if {[file exists $user_post_implementation_file]} {
242  Msg Info "Sourcing user post-implementation file $user_post_implementation_file"
243  source $user_post_implementation_file
244 }
245 
246 cd $old_path
247 Msg Info "All done."