cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
cf_sratool.py
Go to the documentation of this file.
1 # /*******************************************************************************
2 # * Copyright 2016 -- 2022 IBM Corporation
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 
17 # *
18 # * cloudFPGA
19 # * =============================================
20 # * Created: Nov 2021
21 # * Authors: FAB, WEI, NGL, DID
22 # *
23 # * Description:
24 # * Python script to build and mange cloudFPGA projects (cFps).
25 # *
26 # *
27 
28 import json
29 import os
30 import sys
31 from docopt import docopt
32 
33 __version__ = 0.3
34 
35 docstr = """sra tools -- cloudFPGA Project Build & Management Framework
36 
37 Usage:
38  sra update-shell
39  sra config (add-role <path-to-role-dir> <name> | use-role <name> | del-role <name> | show )
40  sra build (proj | monolithic | pr) [--role=<name>] [--incr] [--debug]
41  sra clean [--full]
42  sra admin (build (pr_full | pr_flash) | full_clean | set-2nd-role <name> | write-to-json)
43  sra open-gui
44 
45  sra -h|--help
46  sra -v|--version
47 
48 Commands:
49  update-shell Checks for a new static DCP of the selected shell and downloads it if necessary.
50  config Changes the project configuration (cFp.json) by adding, deleting and selecting Roles.
51  build Builds a new FPGA design, if not specified otherwise, the last selected/activated Role will be used.
52  clean Deletes temporary build files.
53  admin Provide additional commands for cFDK Shell developers.
54  open-gui Opens the graphical user interface of the design (i.e. Vivado).
55 
56 Options:
57  -h --help Show this screen.
58  -v --version Show version.
59 
60  add-role <path-to-role-dir> <name> Adds a new Role to the project configuration (cFp.json), to be used for builds.
61  The <path-to-role-dir> *must* be within the <cFp-Root>/ROLE/ directory.
62  use-role <name> Sets the specified Role as active. Only one Role can be active at a time, so
63  the previous activated Role will be deactivated. By default, the activated Role
64  will be used for the build process.
65  del-role <name> Deletes a Role from the configuration, it wil *not* delete files.
66  show Show current project configuration.
67 
68  proj Create only the Vivado project files for a monolithic build and exit (useful if
69  one wants to use the Vivado GUI).
70  monolithic Invokes the `monolithic` build flow, using the activated Role.
71  pr Invokes the `pr` (partial reconfiguration) build flow, using the activated
72  Role and the latest downloaded static DCP (downloads a new DCP, if none is
73  present).
74  --role=<name> Uses the specified Role for the build process, not the current active Role.
75  --incr Enables the incremental build feature for monolithic designs.
76  --debug Adds debug probes during the build process, as specified in TOP/xdc/debug.xdc.
77 
78  --full Makes a full clean, also removing generated HLS cores from the IP library.
79 
80 Copyright IBM Research, licensed under the Apache License 2.0.
81 Contact: {ngl,fab,wei, did, hle}@zurich.ibm.com
82 """
83 
84 __cfp_json_name__ = 'cFp.json'
85 __to_be_defined_key__ = 'to-be-defined'
86 __none_key__ = 'None'
87 __sra_key__ = 'srat-conf'
88 __role_config_dict_templ__ = {'name': "", 'path': ""}
89 __default_role_entry__ = {'name': 'default', 'path': ""}
90 __sra_dict_template__ = {'version': __version__, 'roles': [__default_role_entry__],
91  'active_role': __to_be_defined_key__}
92 __admin_key__ = 'admin'
93 __admin_dict_template__ = {'2nd-role': __to_be_defined_key__}
94 __shell_type_key__ = 'cFpSRAtype'
95 __mod_type_key__ = 'cFpMOD'
96 __dcps_folder_name__ = '/dcps/'
97 __sratool_user_env_key__ = 'cFpSraToolsUserFlowActive'
98 
99 
100 def get_cfp_role_path(cfp_root, role_entry):
101  role_path = os.path.abspath(cfp_root + '/ROLE/' + role_entry['path'])
102  return role_path
103 
104 
105 def handle_arguments(arguments, cfenv_small_py_bin, cfp_env_folder, cfp_root, cFp_data, dcp_file_path, meta_file_path):
106  if arguments['update-shell']:
107  # os.system("{} {}/get_latest_dcp.py".format(os.environ['cFsysPy3_cmd'], cfp_env_folder))
108  rc = os.system("{} {}/get_latest_dcp.py".format(cfenv_small_py_bin, cfp_env_folder))
109  return cFp_data, False, rc
110  if arguments['clean']:
111  if arguments['--full']:
112  rc = os.system('cd {}; make full_clean'.format(cfp_root))
113  else:
114  rc = os.system('cd {}; make clean'.format(cfp_root))
115  return cFp_data, False, rc
116  if arguments['open-gui']:
117  rc = os.system('cd; vivado xpr/top{}.xpr'.format(cfp_root, cFp_data[__mod_type_key__]))
118  return cFp_data, False, rc
119 
120  if arguments['config']:
121  if arguments['show']:
122  print("[sra:INFO] The following roles are currently defined:")
123  if len(cFp_data[__sra_key__]['roles']) == 0:
124  print("\tnone")
125  else:
126  for e in cFp_data[__sra_key__]['roles']:
127  print("\tRole {} in directory {}".format(e['name'],
128  os.path.abspath(cfp_root + '/ROLE/' + e['path'])))
129  if cFp_data[__sra_key__]['active_role'] != __to_be_defined_key__:
130  print("\tCurrent active role: {}".format(cFp_data[__sra_key__]['active_role']))
131  return cFp_data, False, 0
132  if arguments['add-role']:
133  relative_path = ''
134  path_parts = arguments['<path-to-role-dir>'].split('/')
135  for pp in path_parts:
136  if len(pp) == 0:
137  break
138  if pp == '.':
139  continue
140  if pp == 'ROLE':
141  # maybe they specified it with relative path/tab completion
142  continue
143  relative_path += pp + '/' # must be there also for the last entry
144  new_abs_path = os.path.abspath(cfp_root + '/ROLE/' + relative_path)
145  if not os.path.isdir(new_abs_path):
146  print("[ERROR] The specified path {} seems not to be a valid directory.".format(new_abs_path))
147  print("\t(Hint: All roles for this project must be in/below the {} directory.)"
148  .format(os.path.abspath(cfp_root + '/ROLE/')))
149  return cFp_data, False, -1
150  new_role_dict = {'name': arguments['<name>'], 'path': relative_path}
151  for existing_entry in cFp_data[__sra_key__]['roles']:
152  if existing_entry['name'] == new_role_dict['name']:
153  print("[sra:ERROR] A role with the name {} exists already.".format(new_role_dict['name']))
154  return cFp_data, False, -1
155  cFp_data[__sra_key__]['roles'].append(new_role_dict)
156  return cFp_data, True, 0
157  if arguments['use-role']:
158  new_active_role = arguments['<name>']
159  is_existing = False
160  for existing_entry in cFp_data[__sra_key__]['roles']:
161  if existing_entry['name'] == new_active_role:
162  is_existing = True
163  break
164  if is_existing:
165  cFp_data[__sra_key__]['active_role'] = new_active_role
166  return cFp_data, True, 0
167  else:
168  print("[sra:ERROR] No role with name {} is defined.".format(new_active_role))
169  return cFp_data, False, -1
170  if arguments['del-role']:
171  del_role = arguments['<name>']
172  is_existing = False
173  entry_to_delete = None
174  for existing_entry in cFp_data[__sra_key__]['roles']:
175  if existing_entry['name'] == del_role:
176  is_existing = True
177  entry_to_delete = existing_entry
178  break
179  if is_existing:
180  idx_to_del = cFp_data[__sra_key__]['roles'].index(entry_to_delete)
181  del cFp_data[__sra_key__]['roles'][idx_to_del]
182  return cFp_data, True, 0
183  else:
184  print("[sra:ERROR] No role with name {} is defined.".format(del_role))
185  return cFp_data, False, -1
186 
187  if arguments['build'] and not arguments['admin']:
188  rc = -1
189  cur_active_role = cFp_data[__sra_key__]['active_role']
190  if arguments['--role'] is not None:
191  cur_active_role = arguments['--role']
192  if cur_active_role == __none_key__ or cur_active_role == __to_be_defined_key__:
193  print("[sra:ERROR] A role must be set active first, or defined using the --role option.")
194  return cFp_data, False, -1
195  is_existing = False
196  cur_active_role_dict = None
197  for existing_entry in cFp_data[__sra_key__]['roles']:
198  if existing_entry['name'] == cur_active_role:
199  is_existing = True
200  cur_active_role_dict = existing_entry
201  break
202  if not is_existing:
203  print("[sra:ERROR] No role with name {} is defined.".format(cur_active_role))
204  return cFp_data, False, -1
205  with_debug = False
206  with_incr = False
207  if arguments['--debug']:
208  with_debug = True
209  if arguments['--incr']:
210  with_incr = True
211  if arguments['proj']:
212  print("[sra:INFO] Starting to create the project files for a monolithic design with role {}..."
213  .format(cur_active_role))
214  # start make and OVERWRITE the environment variables
215  rc = os.system('cd {}; export {}=true; export roleName1={}; export usedRoleDir={}; make monolithic_proj'
216  .format(cfp_root, __sratool_user_env_key__, cur_active_role_dict['name'],
217  get_cfp_role_path(cfp_root, cur_active_role_dict)))
218  elif arguments['monolithic']:
219  info_str = "[sra:INFO] Starting to to build a monolithic design with role {}" \
220  .format(cur_active_role)
221  make_cmd = 'monolithic'
222  if with_incr:
223  make_cmd += '_incr'
224  info_str += ' using the incremental build feature'
225  if with_debug:
226  make_cmd += '_debug'
227  info_str += ' and inserting debug probes (as defined in {})' \
228  .format(os.path.abspath(cfp_root + '/TOP/xdc/debug.xdc'))
229  info_str += '...'
230  print(info_str)
231  # start make and OVERWRITE the environment variables
232  rc = os.system('cd {}; export {}=true; export roleName1={}; export usedRoleDir={}; make {}'
233  .format(cfp_root, __sratool_user_env_key__, cur_active_role_dict['name'],
234  get_cfp_role_path(cfp_root, cur_active_role_dict), make_cmd))
235  elif arguments['pr']:
236  if with_incr:
237  print("[sra:INFO] Incremental compile with a partial reconfiguration design is not (yet) " +
238  "supported (but anyhow, just the role is build).")
239  info_str = "[sra:INFO] Starting to to build a partial reconfiguration design for role {}" \
240  .format(cur_active_role)
241  make_cmd = 'pr2_only'
242  if with_debug:
243  print("[sra:ERROR] NOT-YET-IMPLEMENTED (pr build with debug probes).")
244  return cFp_data, False, -1
245  # check for dcp
246  if not os.path.isfile(dcp_file_path) or not os.path.isfile(meta_file_path):
247  # os.system("{} {}/get_latest_dcp.py".format(os.environ['cFsysPy3_cmd'], cfp_env_folder))
248  rc = os.system("{} {}/get_latest_dcp.py".format(cfenv_small_py_bin, cfp_env_folder))
249  if (not os.path.isfile(dcp_file_path)) or (rc != 0):
250  print("sra:ERROR] No DCP present, can not build pr designs. Stop.")
251  return cFp_data, False, -1
252  info_str += '...'
253  print(info_str)
254  # start make and OVERWRITE the environment variables
255  rc = os.system('cd {}; export {}=true; export roleName1={}; export usedRoleDir={}; \
256  export roleName2={}; export usedRole2Dir={}; make {}'
257  .format(cfp_root, __sratool_user_env_key__,
258  # cur_active_role_dict['name'], get_cfp_role_path(cfp_root, cur_active_role_dict),
259  __to_be_defined_key__, __to_be_defined_key__, # role 1 should be totally ignored?
260  cur_active_role_dict['name'], get_cfp_role_path(cfp_root, cur_active_role_dict),
261  make_cmd))
262  return cFp_data, False, rc
263 
264  if arguments['admin']:
265  if arguments['full_clean']:
266  rc = os.system('cd {}; make full_clean'.format(cfp_root))
267  return cFp_data, False, rc
268  if arguments['set-2nd-role']:
269  cFp_data[__sra_key__][__admin_key__]['2nd-role'] = arguments['<name>']
270  return cFp_data, True, 0
271  if arguments['write-to-json']:
272  print("[sra:INFO] Writing current role setting to cFp.json.")
273  cur_active_role_1 = cFp_data[__sra_key__]['active_role']
274  if cur_active_role_1 == __none_key__ or cur_active_role_1 == __to_be_defined_key__:
275  print("[sra:ERROR] A role must be set active first.")
276  return cFp_data, False, -1
277  is_existing = False
278  cur_active_role_dict_1 = None
279  for existing_entry in cFp_data[__sra_key__]['roles']:
280  if existing_entry['name'] == cur_active_role_1:
281  is_existing = True
282  cur_active_role_dict_1 = existing_entry
283  break
284  if not is_existing:
285  print("[sra:ERROR] No role with name {} is defined.".format(cur_active_role_1))
286  return cFp_data, False, -1
287  write_2nd_role = True
288  cur_active_role_2 = cFp_data[__sra_key__][__admin_key__]['2nd-role']
289  cur_active_role_dict_2 = None
290  if cur_active_role_2 == __none_key__ or cur_active_role_2 == __to_be_defined_key__:
291  write_2nd_role = False
292  is_existing = False
293  for existing_entry in cFp_data[__sra_key__]['roles']:
294  if existing_entry['name'] == cur_active_role_2:
295  is_existing = True
296  cur_active_role_dict_2 = existing_entry
297  break
298  if not is_existing:
299  write_2nd_role = False
300  # update cFp struct
301  cFp_data['roleName1'] = cur_active_role_dict_1['name']
302  cFp_data['usedRoleDir'] = cur_active_role_dict_1['path']
303  if write_2nd_role:
304  cFp_data['roleName2'] = cur_active_role_dict_2['name']
305  cFp_data['usedRoleDir2'] = cur_active_role_dict_2['path']
306  return cFp_data, True, 0
307  elif arguments['build']:
308  cur_active_role = cFp_data[__sra_key__]['active_role']
309  # if arguments['--role'] is not None:
310  # cur_active_role = arguments['--role']
311  if cur_active_role == __none_key__ or cur_active_role == __to_be_defined_key__:
312  # print("[sra:ERROR] A role must be set active first, or defined using the --role option.")
313  print("[sra:ERROR] A role must be set active first.")
314  return cFp_data, False, -1
315  is_existing = False
316  cur_active_role_dict = None
317  for existing_entry in cFp_data[__sra_key__]['roles']:
318  if existing_entry['name'] == cur_active_role:
319  is_existing = True
320  cur_active_role_dict = existing_entry
321  break
322  if not is_existing:
323  print("[sra:ERROR] No role with name {} is defined.".format(cur_active_role))
324  return cFp_data, False, -1
325  if arguments['pr_flash']:
326  # no 2nd role is required
327  info_str = "[sra:INFO] Starting to build all files for a new platform logic, using role {}" \
328  .format(cur_active_role)
329  make_cmd = 'pr_flash'
330  info_str += '...'
331  print(info_str)
332  # start make and OVERWRITE the environment variables
333  # no __sratool_user_env_key__ in admin case
334  rc = os.system('cd {}; export roleName1={}; export usedRoleDir={}; \
335  export roleName2={}; export usedRole2Dir={}; make {}'
336  .format(cfp_root,
337  cur_active_role_dict['name'], get_cfp_role_path(cfp_root, cur_active_role_dict),
338  __to_be_defined_key__, __to_be_defined_key__,
339  # role 2 should be totally ignored?
340  # cur_active_role_dict['name'], get_cfp_role_path(cfp_root, cur_active_role_dict),
341  make_cmd))
342  return cFp_data, False, rc
343  elif arguments['pr_full']:
344  # two active roles are required
345  cur_active_role_2 = cFp_data[__sra_key__][__admin_key__]['2nd-role']
346  if cur_active_role_2 == __none_key__ or cur_active_role_2 == __to_be_defined_key__:
347  print("[sra:ERROR] A 2nd role must be set active first.")
348  return cFp_data, False, -1
349  is_existing = False
350  cur_active_role_dict_2 = None
351  for existing_entry in cFp_data[__sra_key__]['roles']:
352  if existing_entry['name'] == cur_active_role_2:
353  is_existing = True
354  cur_active_role_dict_2 = existing_entry
355  break
356  if not is_existing:
357  print("[sra:ERROR] No role with name {} is defined.".format(cur_active_role_2))
358  return cFp_data, False, -1
359  info_str = "[sra:INFO] Starting to build a *complete* partial reconfiguration design, " + \
360  "using roles {} and {}" \
361  .format(cur_active_role, cur_active_role_2)
362  make_cmd = 'pr_full'
363  info_str += '...'
364  print(info_str)
365  # start make and OVERWRITE the environment variables
366  # no __sratool_user_env_key__ in admin case
367  rc = os.system('cd {}; export roleName1={}; export usedRoleDir={}; \
368  export roleName2={}; export usedRole2Dir={}; make {}'
369  .format(cfp_root,
370  cur_active_role_dict['name'], get_cfp_role_path(cfp_root, cur_active_role_dict),
371  cur_active_role_dict_2['name'],
372  get_cfp_role_path(cfp_root, cur_active_role_dict_2),
373  make_cmd))
374  return cFp_data, False, rc
375  return cFp_data, False, 0
376 
377 
378 def main():
379  arguments = docopt(docstr, version=__version__)
380 
381  # first, get and parse cFp.json
382  cfp_root = os.environ['cFpRootDir']
383  cfp_env_folder = os.path.abspath(cfp_root + '/env/')
384  cfenv_small_py_bin = os.path.abspath(cfp_root + 'env/cfenv-small/bin/python3')
385  cfp_json_file = os.path.abspath(cfp_root + '/' + __cfp_json_name__)
386  with open(cfp_json_file, 'r') as json_file:
387  cFp_data = json.load(json_file)
388 
389  store_updated_cfp_json = False
390  # check for structure
391  if __sra_key__ not in cFp_data:
392  cFp_data[__sra_key__] = __sra_dict_template__
393  store_updated_cfp_json = True
394 
395  if arguments['admin']:
396  if __admin_key__ not in cFp_data[__sra_key__]:
397  cFp_data[__sra_key__][__admin_key__] = __admin_dict_template__
398  store_updated_cfp_json = True
399 
400  # check for used role 1
401  if cFp_data['roleName1'] == __to_be_defined_key__:
402  store_updated_cfp_json = True
403  cFp_data['roleName1'] = 'default'
404  cFp_data['usedRoleDir'] = '' # default ROLE/ folder, no hierarchy
405 
406  dcps_folder = os.path.abspath(cfp_root + __dcps_folder_name__)
407  dcp_file_name = "3_top{}_STATIC.dcp".format(cFp_data[__mod_type_key__])
408  dcp_file_path = os.path.abspath(dcps_folder + "/" + dcp_file_name)
409  meta_file_name = "3_top{}_STATIC.json".format(cFp_data[__mod_type_key__])
410  meta_file_path = os.path.abspath(dcps_folder + "/" + meta_file_name)
411 
412  # print(arguments)
413  cFp_data_new, data_updated, rc = handle_arguments(arguments, cfenv_small_py_bin, cfp_env_folder, cfp_root,
414  cFp_data, dcp_file_path, meta_file_path)
415 
416  # finally
417  if store_updated_cfp_json or data_updated:
418  # always update version
419  # cFp_data[__sra_key__]['version'] = __version__
420  cFp_data_new[__sra_key__]['version'] = __version__
421  with open(cfp_json_file, 'w') as json_file:
422  # json.dump(cFp_data, json_file, indent=4)
423  json.dump(cFp_data_new, json_file, indent=4)
424 
425  if 'SraToolShowHint' in os.environ:
426  if os.environ['SraToolShowHint'] == "True" and not 'SraToolHintWasShown' in os.environ:
427  srat_fyi = "\npsst...just FYI: If you want to use the new 'sra' command without typing always " \
428  "'./' first, \nyou can add the following to your '~/.bashrc' and activate it with 'source " \
429  "~/.bashrc' " \
430  "afterwards:\n"
431  srat_bashrc = '--------------\n' \
432  'srafunc(){\n\tcur_pwd=$(pwd)\n\tsrat=$cur_pwd/sra\n\tif [ -f "$srat" ]; then\n\t\t$srat ' \
433  '$@\n\telse' \
434  '\n\t\techo "Error: No cloudFPGA sra tools present in this folder."\n\tfi\n}\n\nalias ' \
435  'sra=srafunc\n' \
436  '--------------\n'
437  print(srat_fyi + srat_bashrc)
438  os.system('cd {}/env; echo "export SraToolHintWasShown=1" >> this_machine_env.sh'
439  .format(cfp_root))
440  return rc
441 
442 
443 if __name__ == '__main__':
444  # vitrualenv is activated by the bash script
445  if not (hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)):
446  # This works with virtualenv for Python 3 and 2 and also for the venv module in Python 3
447  print("[sra:ERROR] It looks like this sra isn't running in a virtual environment. STOP.")
448  sys.exit(1)
449  mrc = main()
450  if mrc != 0:
451  if mrc != -1 and mrc != 1:
452  print("[sra:DEBUG] Internal error code returned: {}\n".format(mrc))
453  sys.exit(1)
454  sys.exit(0)
455 
def main()
Definition: cf_sratool.py:378
def get_cfp_role_path(cfp_root, role_entry)
Definition: cf_sratool.py:100
def handle_arguments(arguments, cfenv_small_py_bin, cfp_env_folder, cfp_root, cFp_data, dcp_file_path, meta_file_path)
Definition: cf_sratool.py:105