opencos-eda 0.3.3__py3-none-any.whl → 0.3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
opencos/util.py CHANGED
@@ -21,6 +21,7 @@ from dotenv import load_dotenv
21
21
  from supports_color import supportsColor
22
22
 
23
23
  from opencos.utils import status_constants
24
+ from opencos.utils.str_helpers import strip_ansi_color
24
25
 
25
26
  global_exit_allowed = False # pylint: disable=invalid-name
26
27
  progname = "UNKNOWN" # pylint: disable=invalid-name
@@ -31,6 +32,7 @@ env_files_loaded = set() # pylint: disable=invalid-name
31
32
 
32
33
  args = { # pylint: disable=invalid-name
33
34
  'color' : bool(supportsColor.stdout),
35
+ 'emoji' : bool(getattr(supportsColor.stdout, 'level', 0) >= 2),
34
36
  'quiet' : False,
35
37
  'verbose' : False,
36
38
  'debug' : False,
@@ -43,16 +45,21 @@ args = { # pylint: disable=invalid-name
43
45
  max_error_code = 0 # pylint: disable=invalid-name
44
46
 
45
47
  class Colors:
46
- '''Namespace class for color printing help'''
48
+ '''Namespace class for color printing help
49
+
50
+ Avoid calling these directly, other than perhapas calling info(*txt, color=Colors.red)
51
+ with 'color' set. It is preferred for outside callers to use one of the print_<color>(..)
52
+ functions, or one of info|warning|error|debug
53
+ '''
47
54
  red = "\x1B[31m"
48
55
  green = "\x1B[32m"
49
- orange = "\x1B[33m"
50
- yellow = "\x1B[39m"
56
+ yellow = "\x1B[33m" # This looks orange, but it's techincally yellow
57
+ foreground = "\x1B[39m"
51
58
  normal = "\x1B[0m"
52
59
 
53
60
  @staticmethod
54
61
  def color_text(text: str, color: str) -> str:
55
- '''Wraps 'text' (str) with color (one of red|green|orange|yellow) prefix and
62
+ '''Wraps 'text' (str) with color (one of red|green|yellow|foreground) prefix and
56
63
 
57
64
  color (normal) suffix. Disables color prefix/suffix wrapping if args['color']=False
58
65
  '''
@@ -60,29 +67,13 @@ class Colors:
60
67
  return color + text + "\x1B[0m" # (normal)
61
68
  return text
62
69
 
63
- def red_text(text: str) -> str:
64
- '''Wraps text for printing as red, disabled if global args['color']=False'''
65
- if args['color']:
66
- return Colors.red + text + Colors.normal
67
- return text
68
-
69
- def green_text(text: str) -> str:
70
- '''Wraps text for printing as green, disabled if global args['color']=False'''
71
- if args['color']:
72
- return Colors.green + text + Colors.normal
73
- return text
74
-
75
- def orange_text(text: str) -> str:
76
- '''Wraps text for printing as orange, disabled if global args['color']=False'''
77
- if args['color']:
78
- return Colors.orange + text + Colors.normal
79
- return text
80
-
81
- def yellow_text(text: str) -> str:
82
- '''Wraps text for printing as yellow, disabled if global args['color']=False'''
83
- if args['color']:
84
- return Colors.yellow + text + Colors.normal
85
- return text
70
+
71
+ def safe_emoji(emoji: str, default: str = '') -> str:
72
+ '''Returns emoji character if args['emoji'] is True'''
73
+ if args['emoji']:
74
+ return emoji
75
+ return default
76
+
86
77
 
87
78
  class ArtifactTypes(Enum):
88
79
  '''Types that are allow-listed for artifacts.add* methods. If you don't use one of
@@ -342,7 +333,7 @@ def get_argparse_bool_action_kwargs() -> dict:
342
333
  def get_argparser() -> argparse.ArgumentParser:
343
334
  '''Returns the opencos.util ArgumentParser'''
344
335
  parser = argparse.ArgumentParser(
345
- prog='opencos common options', add_help=False, allow_abbrev=False
336
+ prog=f'{safe_emoji("🔎 ")}opencos common options', add_help=False, allow_abbrev=False
346
337
  )
347
338
  # We set allow_abbrev=False so --force-logfile won't try to attempt parsing shorter similarly
348
339
  # named args like --force, we want those to go to unparsed list.
@@ -354,6 +345,8 @@ def get_argparser() -> argparse.ArgumentParser:
354
345
  parser.add_argument('--version', default=False, action='store_true')
355
346
  parser.add_argument('--color', **bool_action_kwargs, default=bool(supportsColor.stdout),
356
347
  help='Use shell colors for info/warning/error messaging')
348
+ parser.add_argument('--emoji', **bool_action_kwargs, default=args['emoji'],
349
+ help=f'Support emojis in terminal{safe_emoji(" 💪")}')
357
350
  parser.add_argument('--quiet', **bool_action_kwargs, default=args['quiet'],
358
351
  help='Do not display info messaging')
359
352
  parser.add_argument('--verbose', **bool_action_kwargs, default=args['verbose'],
@@ -383,8 +376,10 @@ def get_argparser() -> argparse.ArgumentParser:
383
376
  'Input .f file to be expanded as eda'
384
377
  ' args/defines/incdirs/files/targets'))
385
378
  parser.add_argument('--env-file', default=[], action='append',
386
- help='dotenv file(s) to pass ENV vars, (default: .env, loaded last)'
387
- )
379
+ help=(
380
+ "dotenv file(s) to pass ENV vars, (default: .env loaded first,"
381
+ " subsequent files' vars override .env"
382
+ ))
388
383
  return parser
389
384
 
390
385
 
@@ -407,12 +402,25 @@ def get_argparser_short_help(parser: object = None) -> str:
407
402
  '''Returns short help for our ArgumentParser'''
408
403
  if not parser:
409
404
  parser = get_argparser()
410
- full_lines = parser.format_help().split('\n')
405
+
406
+ if not args['color']:
407
+ # Since python3.14 doesn't care about our custom color settings,
408
+ # need to remove any ANSI colors from argparse help formatter:
409
+ full_lines = strip_ansi_color(parser.format_help()).split('\n')
410
+ else:
411
+ full_lines = parser.format_help().split('\n')
412
+
411
413
  lineno = 0
412
414
  for lineno, line in enumerate(full_lines):
413
- if line.startswith('options:'):
415
+ # Again, strip any ANSI colors when searching for starting text:
416
+ # - options:
417
+ # - optional arguments:
418
+ if args['color']:
419
+ line = strip_ansi_color(line)
420
+ if any(line.startswith(x) for x in ('options:', 'optional arguments:')):
414
421
  break
415
- # skip the line that says 'options:', repalce with the progname:
422
+
423
+ # skip the line that says 'options:', replace with the progname:
416
424
  return f'{parser.prog}:\n' + '\n'.join(full_lines[lineno + 1:])
417
425
 
418
426
 
@@ -432,11 +440,12 @@ def process_token(arg: list) -> bool:
432
440
  def load_env_file(env_file: str) -> None:
433
441
  '''Handles .env file (from util CLI args --env-file)'''
434
442
  if os.path.isfile(env_file):
435
- load_dotenv(env_file)
443
+ load_dotenv(env_file, override=True)
436
444
  env_files_loaded.add(os.path.abspath(env_file))
437
445
  else:
438
446
  warning(f'--env-file {env_file} does not exist and is not loaded.')
439
447
 
448
+
440
449
  def patch_args_for_dir(tokens: list, patch_dir: str, caller_info: str) -> list:
441
450
  '''Given list of args, attempt to correct for relative dir'''
442
451
 
@@ -536,8 +545,10 @@ def process_tokens( # pylint: disable=too-many-branches
536
545
  parser.add_argument('--debug-level', type=int, default=0,
537
546
  help='Set debug level messaging (default: 0)')
538
547
  parser.add_argument('--env-file', default=[], action='append',
539
- help='dotenv file(s) to pass ENV vars, (default: .env, loaded last)'
540
- )
548
+ help=(
549
+ "dotenv file(s) to pass ENV vars, (default: .env loaded first,"
550
+ " subsequent files' vars override .env"
551
+ ))
541
552
  parser.add_argument('-f', '--input-file', default=[], action='append',
542
553
  help=(
543
554
  'Input .f file to be expanded as eda args, defines, incdirs,'
@@ -553,7 +564,7 @@ def process_tokens( # pylint: disable=too-many-branches
553
564
  debug(f'util.process_tokens: {parsed=} {unparsed=} from: {tokens}')
554
565
 
555
566
  if os.path.isfile(str(Path('.env'))):
556
- parsed.env_file.append('.env')
567
+ parsed.env_file.insert(0, '.env')
557
568
  if parsed.env_file:
558
569
  for env_file in parsed.env_file:
559
570
  load_env_file(env_file)
@@ -718,7 +729,7 @@ def print_post(text: str, end: str) -> None:
718
729
 
719
730
 
720
731
  def print_color(text: str, color: str, end: str = '\n') -> None:
721
- '''Note that color(str) must be one of Colors.[red|green|orange|yellow|normal]'''
732
+ '''Note that color(str) must be one of Colors.[red|green|yellow|normal]'''
722
733
  print_pre()
723
734
  print(Colors.color_text(text, color), end=end, flush=True)
724
735
  print_post(text, end)
@@ -726,25 +737,25 @@ def print_color(text: str, color: str, end: str = '\n') -> None:
726
737
  def print_red(text: str, end: str = '\n') -> None:
727
738
  '''Print text as red, goes back to normal color'''
728
739
  print_pre()
729
- print(red_text(text), end=end, flush=True)
740
+ print(Colors.color_text(text, color=Colors.red), end=end, flush=True)
730
741
  print_post(text, end)
731
742
 
732
743
  def print_green(text: str, end: str = '\n') -> None:
733
744
  '''Print text as green, goes back to normal color'''
734
745
  print_pre()
735
- print(green_text(text), end=end, flush=True)
746
+ print(Colors.color_text(text, color=Colors.green), end=end, flush=True)
736
747
  print_post(text, end)
737
748
 
738
- def print_orange(text: str, end: str = '\n') -> None:
739
- '''Print text as orange, goes back to normal color'''
749
+ def print_yellow(text: str, end: str = '\n') -> None:
750
+ '''Print text as yellow, goes back to normal color'''
740
751
  print_pre()
741
- print(orange_text(text), end=end, flush=True)
752
+ print(Colors.color_text(text, color=Colors.yellow), end=end, flush=True)
742
753
  print_post(text, end)
743
754
 
744
- def print_yellow(text: str, end: str = '\n') -> None:
745
- '''Print text as yellow, goes back to normal color'''
755
+ def print_foreground_color(text: str, end: str = '\n') -> None:
756
+ '''Print text as foreground color, goes back to normal color'''
746
757
  print_pre()
747
- print(yellow_text(text), end=end, flush=True)
758
+ print(Colors.color_text(text, color=Colors.foreground), end=end, flush=True)
748
759
  print_post(text, end)
749
760
 
750
761
 
@@ -757,8 +768,11 @@ def set_debug_level(level) -> None:
757
768
  info(f"Set debug level to {debug_level}")
758
769
 
759
770
 
760
- def debug(*text, level: int = 1, start: object = None, end: str = '\n') -> None:
761
- '''Print debug messaging (in yellow if possible). If args['debug'] is false, prints nothing.
771
+ def debug(
772
+ *text, level: int = 1, start: object = None, end: str = '\n', color=Colors.foreground
773
+ ) -> None:
774
+ '''Print debug messaging (in foreground color if possible). If args['debug'] is false,
775
+ prints nothing.
762
776
 
763
777
  *text: (positional str args) to be printed
764
778
  level: (int) debug level to decide if printed or not.
@@ -771,10 +785,10 @@ def debug(*text, level: int = 1, start: object = None, end: str = '\n') -> None:
771
785
  start = "DEBUG: " + (f"[{progname}] " if progname_in_message else "")
772
786
  if args['debug'] and \
773
787
  (((level==1) and args['verbose']) or (debug_level >= level)):
774
- print_yellow(f"{start}{' '.join(list(text))}", end=end)
788
+ print_color(f"{start}{' '.join(list(text))}", color=color, end=end)
775
789
 
776
790
 
777
- def info(*text, start: object = None, end='\n') -> None:
791
+ def info(*text, start: object = None, end='\n', color=Colors.green) -> None:
778
792
  '''Print information messaging (in green if possible). If args['quiet'], prints nothing.
779
793
 
780
794
  *text: (positional str args) to be printed
@@ -786,10 +800,10 @@ def info(*text, start: object = None, end='\n') -> None:
786
800
  if start is None:
787
801
  start = "INFO: " + (f"[{progname}] " if progname_in_message else "")
788
802
  if not args['quiet']:
789
- print_green(f"{start}{' '.join(list(text))}", end=end)
803
+ print_color(f"{start}{' '.join(list(text))}", color=color, end=end)
790
804
 
791
805
  def warning(*text, start: object = None, end: str = '\n') -> None:
792
- '''Print warning messaging (in orange if possible).
806
+ '''Print warning messaging (in yellow if possible).
793
807
 
794
808
  *text: (positional str args) to be printed
795
809
  start: (optional str) prefix to message; if None: chooses default start str
@@ -800,7 +814,7 @@ def warning(*text, start: object = None, end: str = '\n') -> None:
800
814
  if start is None:
801
815
  start = "WARNING: " + (f"[{progname}] " if progname_in_message else "")
802
816
  args['warnings'] += 1
803
- print_orange(f"{start}{' '.join(list(text))}", end=end)
817
+ print_yellow(f"{start}{' '.join(list(text))}", end=end)
804
818
 
805
819
 
806
820
  def error(
@@ -823,6 +837,7 @@ def error(
823
837
 
824
838
  if start is None:
825
839
  start = "ERROR: " + (f"[{progname}] " if progname_in_message else "")
840
+ start += safe_emoji("❌ ")
826
841
  args['errors'] += 1
827
842
  max_error_code = max(max_error_code, error_code)
828
843
  print_red(f"{start}{' '.join(list(text))}", end=end)
@@ -856,7 +871,17 @@ def exit( # pylint: disable=redefined-builtin
856
871
 
857
872
  if global_exit_allowed:
858
873
  if not quiet:
859
- info(f"Exiting with {args['warnings']} warnings, {args['errors']} errors")
874
+ info_color = Colors.green
875
+ start = safe_emoji('🔚 ')
876
+ if args['errors']:
877
+ info_color = Colors.red
878
+ start = safe_emoji('❗ ')
879
+ elif args['warnings']:
880
+ info_color = Colors.yellow
881
+ info(
882
+ f"{start}Exiting with {args['warnings']} warnings, {args['errors']} errors",
883
+ color=info_color
884
+ )
860
885
  sys.exit(error_code)
861
886
 
862
887
  if error_code is None:
@@ -992,14 +1017,18 @@ def import_class_from_string(full_class_name: str) -> None:
992
1017
  class ShellCommandList(list):
993
1018
  '''Wrapper around a list, of str that we'll run as a subprocess command
994
1019
 
995
- included member var for tee_path, to save a log from this subprocess commands list
1020
+ included member vars for:
1021
+ - tee_path, to save a log from this subprocess commands list
1022
+ - work_dir - in case we want to run this from non-default location.
996
1023
  '''
997
- def __init__(self, obj: object = None, tee_fpath: str = ''):
1024
+ def __init__(self, obj: object = None, tee_fpath: str = '', work_dir: str = ''):
998
1025
  super().__init__(obj)
999
- for k in ['tee_fpath']:
1026
+ for k in ('tee_fpath', 'work_dir'):
1000
1027
  setattr(self, k, getattr(obj, k, None))
1001
1028
  if tee_fpath:
1002
1029
  self.tee_fpath = tee_fpath
1030
+ if work_dir:
1031
+ self.work_dir = work_dir
1003
1032
 
1004
1033
 
1005
1034
  def write_shell_command_file(
@@ -172,7 +172,7 @@ def pretty_list_columns_manual(data: list, num_columns: int = 4, auto_columns: b
172
172
  pretty_list_columns_manual(data=data, num_columns=num_columns-1, auto_columns=True)
173
173
  )
174
174
  return ret_lines
175
- if max_line_len + max_item_len + _spacing < window_cols:
175
+ if max_line_len + max_item_len + _spacing <= window_cols:
176
176
  # add 1 more column if we're guaranteed to have room.
177
177
  ret_lines.extend(
178
178
  pretty_list_columns_manual(data=data, num_columns=num_columns+1, auto_columns=True)
@@ -198,3 +198,8 @@ def print_columns_manual(data: list, num_columns: int = 4, auto_columns: bool =
198
198
  data=data, num_columns=num_columns, auto_columns=auto_columns
199
199
  )
200
200
  print('\n'.join(lines))
201
+
202
+ def strip_ansi_color(text: str) -> str:
203
+ '''Strip ANSI color characters from str'''
204
+ ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
205
+ return ansi_escape.sub('', text)
@@ -5,10 +5,18 @@ import shutil
5
5
  import subprocess
6
6
  import sys
7
7
 
8
- from opencos.util import debug, error, info, progname, global_log
8
+ import psutil
9
+ from opencos.util import debug, error, info, warning, progname, global_log
9
10
 
10
11
  IS_WINDOWS = sys.platform.startswith('win')
11
12
 
13
+ # For non-Windows, we track the background parent PIDs, because some tools (vivado XSim,
14
+ # most Modelsim/Questa variants) tend to spawn children PIDs that don't always respond
15
+ # nicely to a friendly *nix SIGTERM. So we'll remember what our parent PIDs are, and
16
+ # eda.py's (or other CLI opencos script) can use signal to cleanup any remaining
17
+ # parents + children using subprocess_helpers.cleanup_all()
18
+ ALL_PARENT_PIDS = set()
19
+
12
20
  def subprocess_run(
13
21
  work_dir: str, command_list: list, fake: bool = False, shell: bool = False
14
22
  ) -> int:
@@ -35,10 +43,11 @@ def subprocess_run(
35
43
 
36
44
  debug(f"subprocess_run: About to call subprocess.run({c}, **{proc_kwargs}")
37
45
  proc = subprocess.run(c, check=True, **proc_kwargs)
46
+ # Note - we do not get PID management for subprocess_run(...)
38
47
  return proc.returncode
39
48
 
40
49
 
41
- def subprocess_run_background(
50
+ def subprocess_run_background( # pylint: disable=too-many-branches
42
51
  work_dir: str, command_list: list, background: bool = True, fake : bool = False,
43
52
  shell: bool = False, tee_fpath: str = ''
44
53
  ) -> (str, str, int):
@@ -76,6 +85,9 @@ def subprocess_run_background(
76
85
 
77
86
  debug(f"subprocess_run_background: about to call subprocess.Popen({c}, **{proc_kwargs})")
78
87
  proc = subprocess.Popen(c, **proc_kwargs) # pylint: disable=consider-using-with
88
+ if not background:
89
+ info(f'PID {proc.pid} for {command_list[0]}')
90
+ add_running_parent_pid(proc.pid)
79
91
 
80
92
  stdout = ''
81
93
  tee_fpath_f = None
@@ -99,10 +111,61 @@ def subprocess_run_background(
99
111
  stdout += line + '\n'
100
112
 
101
113
  proc.communicate()
114
+ remove_completed_parent_pid(proc.pid)
115
+
102
116
  rc = proc.returncode
103
117
  if tee_fpath_f:
104
118
  tee_fpath_f.write(f'INFO: [{progname}] subprocess_run_background: returncode={rc}\n')
105
119
  tee_fpath_f.close()
106
- info('subprocess_run_background: wrote: ' + os.path.abspath(tee_fpath))
120
+ if not background:
121
+ info('subprocess_run_background: wrote: ' + os.path.abspath(tee_fpath))
107
122
 
108
123
  return stdout, '', rc
124
+
125
+
126
+ def add_running_parent_pid(pid: int) -> None:
127
+ '''Adds pid (if still alive) to ALL_PARENT_PIDS'''
128
+ try:
129
+ p = psutil.Process(pid)
130
+ ALL_PARENT_PIDS.add(p.pid)
131
+ except psutil.NoSuchProcess:
132
+ pass
133
+ except Exception as e:
134
+ error(f'{pid=} exception {e}')
135
+
136
+ def remove_completed_parent_pid(pid: int) -> None:
137
+ '''Removes pid (if no longer alive) from ALL_PARENT_PIDS.'''
138
+ try:
139
+ p = psutil.Process(pid)
140
+ warning(f'PID {p.pid} still running')
141
+ except psutil.NoSuchProcess:
142
+ ALL_PARENT_PIDS.remove(pid)
143
+ except Exception as e:
144
+ error(f'{pid=} exception {e}')
145
+
146
+
147
+ def cleanup_all() -> None:
148
+ '''Kills everything from ALL_PARENT_PIDS.'''
149
+ for parent in ALL_PARENT_PIDS:
150
+ kill_proc_tree(parent)
151
+
152
+
153
+ def kill_proc_tree(pid: int, including_parent: bool = True) -> None:
154
+ '''Kills a process and its entire descendant tree'''
155
+ try:
156
+ parent = psutil.Process(pid)
157
+ children = parent.children(recursive=True)
158
+ info(f'{pid=} {parent=} {children=}')
159
+ for child in children:
160
+ if psutil.Process(child.pid):
161
+ info(f'parent {pid=} killing {child=}')
162
+ child.kill()
163
+ _, still_alive = psutil.wait_procs(children, timeout=5)
164
+ if still_alive:
165
+ warning(f'parent {pid=} {still_alive=}')
166
+ if including_parent:
167
+ info(f'parent {pid=} killing {parent=}')
168
+ parent.kill()
169
+ parent.wait(5)
170
+ except psutil.NoSuchProcess:
171
+ pass # Process already terminated
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.3.3
3
+ Version: 0.3.6
4
4
  Summary: A simple Python package for wrapping RTL simuliatons and synthesis
5
5
  Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
6
6
  Project-URL: Homepage, https://github.com/cognichip/opencos
@@ -9,13 +9,22 @@ License-File: LICENSE
9
9
  License-File: LICENSE.spdx
10
10
  Requires-Dist: mergedeep>=1.3.4
11
11
  Requires-Dist: peakrdl>=1.1.0
12
+ Requires-Dist: psutil>=7.0.0
12
13
  Requires-Dist: pyyaml>=6.0.2
13
- Requires-Dist: pytest>=8.3.5
14
14
  Requires-Dist: python-dotenv>=1.0.1
15
15
  Requires-Dist: schema>=0.7.7
16
16
  Requires-Dist: toml>=0.10.2
17
17
  Requires-Dist: yamllint>=1.35.1
18
18
  Requires-Dist: PySerial>=3.5
19
- Requires-Dist: cocotb>=2.0
20
19
  Requires-Dist: supports_color>=0.2.0
20
+ Requires-Dist: cocotb>=2.0
21
+ Requires-Dist: pytest>=8.3.5
22
+ Requires-Dist: coverage>=7.6.1
23
+ Provides-Extra: dev
24
+ Requires-Dist: pylint>=3.0.0; extra == "dev"
25
+ Provides-Extra: docs
26
+ Requires-Dist: mkdocs; extra == "docs"
27
+ Requires-Dist: mkdocs-material; extra == "docs"
28
+ Requires-Dist: mkdocs-wavedrom-plugin; extra == "docs"
29
+ Requires-Dist: mkdocs-plantuml; extra == "docs"
21
30
  Dynamic: license-file
@@ -1,24 +1,24 @@
1
1
  opencos/__init__.py,sha256=RwJA9oc1uUlvNX7v5zoqwjnSRNq2NZwRlHqtS-ICJkI,122
2
2
  opencos/_version.py,sha256=KaWIjS0c08g-C0fgYY1kXwSPqhOFxaq5pYEeoZhOR_I,617
3
3
  opencos/_waves_pkg.sv,sha256=TL5YT9lT-fn2FD54MbVVZROmZ7vtW3ScA_rM2eRzKmU,2068
4
- opencos/deps_schema.py,sha256=VUdXuq43mKfM-U4x7DSA28-MH1Xqxre6V7Ttw2DeOqI,16762
5
- opencos/eda.py,sha256=91E-EsyZS-uRadApP-h2onW6rpvLBnrpJoT_9tRtsS8,23322
6
- opencos/eda_base.py,sha256=jf4t11UPj39swL41z-EJiGTOnHvRScaQc-SAjpZ5XI4,109651
7
- opencos/eda_config.py,sha256=z3yQOPGBX7-yKp6BdQYfJ9eOJf-Jctl-mwCDj3vj2BI,12712
8
- opencos/eda_config_defaults.yml,sha256=Bs9UyXckIpauK_tf27iRkig6hBvsuI-PL7HhgCQpA1k,16168
4
+ opencos/deps_schema.py,sha256=fx1_IJhsDYkUciwwCPTXHP6ftFjTsPVjO4xg12twIjw,17384
5
+ opencos/eda.py,sha256=W7D44uv0rWPaQD6OOaQuotNsDnDdhwAoBi4eI-eHQ9E,23418
6
+ opencos/eda_base.py,sha256=jmqpirBKGKg3BLEgie-OgqYUg-JIpYS6Im491MKWF-M,111801
7
+ opencos/eda_config.py,sha256=cKAUKguoFEvFgvi2c36Osf6W767Y4KYjoagflqVhUSw,14168
8
+ opencos/eda_config_defaults.yml,sha256=tbu7hyfPM0AetW0CTMu4J3umGMpHLn5bs5bkWDyFM3w,16260
9
9
  opencos/eda_config_max_verilator_waivers.yml,sha256=lTAU4IOEbUWVlPzuer1YYhIyxpPINeA4EJqcRIT-Ymk,840
10
10
  opencos/eda_config_reduced.yml,sha256=cQ9jY4J7EvAbeHTiP6bvpDSVJAYiitjLZPSxxLKIEbk,1440
11
11
  opencos/eda_deps_bash_completion.bash,sha256=jMkQKY82HBgOnQeMdA1hMrXguRFtB52SMBxUemKovL4,1958
12
12
  opencos/eda_deps_sanitize.py,sha256=SQjvrte9Hv9JesRY0wljvbdC6pAmLCikI-Wdzzy-D04,1939
13
13
  opencos/eda_extract_targets.py,sha256=POlxZfqf2dNH2nc1CEw5B_53vSHAicSTkpU9_-2_6Zw,2851
14
- opencos/eda_tool_helper.py,sha256=_YgobDLEWW6Fzdr976LxaCDZ4DKRyuMs5CrYQHaTPrU,2558
15
- opencos/export_helper.py,sha256=5BnrkhiieJBgYKAryhXD7HSGtrgvXQpZ8B5ltdrhbRY,22649
14
+ opencos/eda_tool_helper.py,sha256=Xm6nr9XweCjueWFLkrH5U3nK96JGeeh86f2GCPhwY-o,3108
15
+ opencos/export_helper.py,sha256=zDkvsUS6FVrpXl1UTy53QG3CuhYp5FFplI9rRzAE2g8,25395
16
16
  opencos/export_json_convert.py,sha256=tSIMbLFtc_Fo66EhFovMii1v_qJYyFZJrPNnoPdW7L0,4182
17
- opencos/files.py,sha256=AQOnsrvoc0r76LiFrkoMbwOGdUO1FpBiFY_jyyI_ve8,1566
17
+ opencos/files.py,sha256=4fomXM5vyA5FUAImSeAjrPchQPysPSD07c_TLYg5cd8,1617
18
18
  opencos/names.py,sha256=Y2aJ5wgpbNIJ-_P5xUXnHMv_h-zMOX2Rt6iLuduqC1Q,1213
19
19
  opencos/peakrdl_cleanup.py,sha256=vHNGtalTrIVP335PhRjPt9RhoccgpK1HJAi-E4M8Kc8,736
20
20
  opencos/seed.py,sha256=IL9Yg-r9SLSRseMVWaEHmuw2_DNi_eyut11EafoNTsU,942
21
- opencos/util.py,sha256=tgDOvkIGCPfuuJmP1nMsxsxaxmhlH9DOUsJ3SVjtXy4,42123
21
+ opencos/util.py,sha256=h0NfMe4sk5CxEO93z9k7m61kMDbrkbUUrDd9CN2aZAE,43558
22
22
  opencos/commands/__init__.py,sha256=oOOQmn5_jHAMSOfA3swJJ7mdoyHsJA0lJwKPTudlTns,1125
23
23
  opencos/commands/build.py,sha256=mvJYxk5J15k0Cr8R7oIdIIdsEtWV3gE-LnPweVwtSDo,1487
24
24
  opencos/commands/deps_help.py,sha256=WDrU7H9sypzDAxe_CHqhW5B_scbQMzBEdf-v-Jcfd5Q,10682
@@ -31,27 +31,27 @@ opencos/commands/multi.py,sha256=ZkG1b_qhhxXw-Lm2S4J-i576Ai5iJA09UEIDj94FOVM,274
31
31
  opencos/commands/open.py,sha256=XckvKUNwvc5KHbYGV-eQ2i0WG4X-yckroDaMC610MB4,804
32
32
  opencos/commands/proj.py,sha256=cExW9ZZkw6nkpVyNfeQzJADzmPtbYgBgWml82tqO6jY,1158
33
33
  opencos/commands/shell.py,sha256=upHpFs8Gdtzi-boVXwsC-QzEsnvtoZNMAu4oN10kdxw,7801
34
- opencos/commands/sim.py,sha256=NQJbjfGEgt4zHtOQn5HnaCiBnaDyEkpoDJvnAR4TOxE,21166
34
+ opencos/commands/sim.py,sha256=3mASPqPiCYzoVLstftcOREtMVb0I0RaLo1wiDFl8nhE,21445
35
35
  opencos/commands/sweep.py,sha256=ni4XFgnFF8HLXtwPhETyLWfvc2kgtm4bcxFcKzUhkf0,9343
36
36
  opencos/commands/synth.py,sha256=m4ZwqHgOF5We0XP94F7TQli11WCPlkzhamI4fDfFR1o,4573
37
37
  opencos/commands/targets.py,sha256=_jRNhm2Fqj0fmMvTw6Ba39DCsRHf_r_uZCy_R064kpA,1472
38
38
  opencos/commands/upload.py,sha256=oyImgcEFGxDkdeY9EYyX2R6fTOmN-lTs-HYxAZqXUUo,871
39
39
  opencos/commands/waves.py,sha256=nrp3ALwfJujZns44tgCgia_dEedQyKe0T3fuws8h39U,7697
40
40
  opencos/deps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- opencos/deps/defaults.py,sha256=NXh3V4oInrBVlDw64B2OCI77wzdn1NtaD64srhBnmZU,1486
41
+ opencos/deps/defaults.py,sha256=Z6mIVJEV0zQ9rC-HkQFMBFAkixjqKS1TATPSc27wOeA,1502
42
42
  opencos/deps/deps_commands.py,sha256=q4JfSfzRO2nM2zdNT4enCy33FokEytZYQJn1HJ6osJk,16606
43
43
  opencos/deps/deps_file.py,sha256=YQ5ftYvppRTqUto22r-XDH6-bcMO7cA-WiJ7QzPjljY,17103
44
- opencos/deps/deps_processor.py,sha256=DBaMUEnpoIL4xaNPs2f2AFGcWLST5pP_Qgup9r-8D7M,41403
44
+ opencos/deps/deps_processor.py,sha256=WvG7p4wo_gNjkS2pxySgDmkomioooctJTxMlmP42kfk,42827
45
45
  opencos/hw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  opencos/hw/oc_cli.py,sha256=U1JGlshLZhtd0LgndZFBZVltAj_HemdhbjO_Zo8ZuVM,132252
47
47
  opencos/hw/pcie.py,sha256=VUJljaZJYgScAAx5yn7F6GoA8K9eTcw24otYZbkMpYs,3035
48
48
  opencos/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  opencos/tests/custom_config.yml,sha256=TRoVM9ZFKPOA_8JmlpzaMhnGO1txmaD14N_8P1oqzew,257
50
- opencos/tests/helpers.py,sha256=0KWHUQNXeyQ40F2sdW_R2r0P8qZpV88ndTZmoix3NcU,12474
50
+ opencos/tests/helpers.py,sha256=5EyKRvpNvkJQ4bZcrm340vIZvJ6ed2G6RmWOQAZh7Lk,14113
51
51
  opencos/tests/test_build.py,sha256=FQAxOpLVQShAHD_L5rqJctPeSAoqoOCNFI0RXflLuY0,387
52
52
  opencos/tests/test_deps_helpers.py,sha256=uQZxleh6aKO-mZQhagHh5xLIBbpQ8dav7-5D0eemq_g,8164
53
53
  opencos/tests/test_deps_schema.py,sha256=T3P9KjaMyKsk8b7snNVvNSsom2hIJcg6Z9apYiXoH9Y,941
54
- opencos/tests/test_eda.py,sha256=PhAFCqoZxUhjuSvNeHpTHdwt8UhHjRdVppesgcWBX64,37407
54
+ opencos/tests/test_eda.py,sha256=8X6kej5-uPv2l0xeWOhitCE640XZtiGLwzCktra_Ehg,37784
55
55
  opencos/tests/test_eda_elab.py,sha256=AjU4WMYtFoHpNe1Z4yWWpxDKy4V_hAjL5rl3jqphZrk,3179
56
56
  opencos/tests/test_eda_synth.py,sha256=BtBrNVJ9C-LJt3K0wNNS5ukEVrET16AbRXl2IzxudJ8,5744
57
57
  opencos/tests/test_oc_cli.py,sha256=w-F-LjSSWVql3D2WG8tcV4_C52i-hL_2WT3oDpKQn9s,734
@@ -64,34 +64,34 @@ opencos/tests/deps_files/non_sv_reqs/DEPS.yml,sha256=VZkahO1AKhD9GUV5lK8VwUONEi5
64
64
  opencos/tests/deps_files/tags_with_tools/DEPS.yml,sha256=-5U1qfJElgpVhmkLEu3lYuvDYva8kDlt6JOdB9jidmc,1377
65
65
  opencos/tests/deps_files/test_err_fatal/DEPS.yml,sha256=GnXIUJvshQWR9PlYxX67T53ejf5KhDbtD8sUJB4Rwd0,95
66
66
  opencos/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
- opencos/tools/cocotb.py,sha256=hlBR6M4k_amOOWa7cZDuw9fTGJtJha1jSpGgyJN8Iis,17734
67
+ opencos/tools/cocotb.py,sha256=bR97Mb87D0HPdoV82rLT7oszPLmdIPzYU1IEBaGnuXg,20544
68
68
  opencos/tools/invio.py,sha256=S2ChWr8xMZHSOOhX2hGKQhMmtQY2potVQjc-lsMg73o,3299
69
69
  opencos/tools/invio_helpers.py,sha256=86WOGmSf4m_lEqBtK3DLjWqI0jnqAWzBEBRYfBUGiSY,8804
70
70
  opencos/tools/invio_yosys.py,sha256=CszGeTdE1ilnMmWPLW77BrtobbsGb1CKXqot0hGimFU,5996
71
- opencos/tools/iverilog.py,sha256=8dK4z8ktbNYS9cW5QQPm586WoE-pSmOAVJfXajw-Sbg,6420
71
+ opencos/tools/iverilog.py,sha256=3IQIZVDioChKEJIVVJki-q7NlvBg0k3n61oM7ltG9c8,6551
72
72
  opencos/tools/modelsim_ase.py,sha256=Jt-6N3BZZyu25fT1ehFQLRUTVvrcCo4e2Gl7UtsQcuk,17834
73
73
  opencos/tools/quartus.py,sha256=_TfmPSYpbhmDLw7Dur-rRP0iGwv9hhQ6E5G-XLiYPEM,30486
74
74
  opencos/tools/questa.py,sha256=nHImM0Wydcf4YHGibHmQAwmqKHmMxKZUqY-E-vz1o8M,9827
75
75
  opencos/tools/questa_fse.py,sha256=hytkeuGg4qImj7rStV1i2kxkz9B0KFheGtcadxmpYAo,2550
76
- opencos/tools/riviera.py,sha256=FAumXIt9u4JXwR5BHe-0APX3K0Lg3RT2lWQ-GNGL3vA,13016
76
+ opencos/tools/riviera.py,sha256=_-vsN7TD6WdW4PVsSJaEhJls3RgXGRowhY_QV1hdFqE,13678
77
77
  opencos/tools/slang.py,sha256=S_vODMT5Zl5vi9FMGHfahp5B0oMNyDIRJXtRAldVCwY,8625
78
78
  opencos/tools/slang_yosys.py,sha256=MKh13eAmLJDkynZiezyT8E2gI4CKnXipzgFCZppaMXo,10230
79
79
  opencos/tools/surelog.py,sha256=S2RAZJyjdISm_tRvAhXbla7_z_tJfotZih5f9Y3m7DQ,5648
80
80
  opencos/tools/tabbycad_yosys.py,sha256=2LePPgYXBVdsy7YcffPIWN-I0B7queLQ_f_pme2SCGw,7803
81
- opencos/tools/verilator.py,sha256=yh3DZXhGxt9RyJVtbn2RoqIiwxZW6_sTiXs8xGf3iNg,21065
82
- opencos/tools/vivado.py,sha256=GQdPd1mp02it_uyhMqpFfJgh0AKIMn-20BhHNEDp5HY,41398
81
+ opencos/tools/verilator.py,sha256=lEsy6VwikTUE3MmuK75NKkRV9YS4MKCPzXH1QZOgcY8,24781
82
+ opencos/tools/vivado.py,sha256=I9yVFK5wnFl1268l8WCk5Xh8wZBlSCODVTg4qStVF8c,41475
83
83
  opencos/tools/yosys.py,sha256=t3Au8gdwTepIKCPCXHpRXEdtmORQK8xqNvF6baIa7DM,28260
84
84
  opencos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
85
  opencos/utils/markup_helpers.py,sha256=A8Ev5UJ4EVKjdcF2g85SQbjdPZR4jGpNqCLaBy_4v7Q,4569
86
86
  opencos/utils/status_constants.py,sha256=na6YsqlsCwIYzTXWE14dPadUYRNTrOS6YTXHCer2NbA,635
87
- opencos/utils/str_helpers.py,sha256=726ScK5-v7QkBi-zqESKZLsOl2_ya4vVJ5ZhxJqmBFo,6440
88
- opencos/utils/subprocess_helpers.py,sha256=xemAGPey6M0sWY_FElvr-Z0phCfdjaC-znP8FKihPaE,3535
87
+ opencos/utils/str_helpers.py,sha256=-hR7MAQLOoY2lIfqtxNtnzb3apeJPkh8shEGFzkwQfs,6637
88
+ opencos/utils/subprocess_helpers.py,sha256=idWc-sy_XJaxIl06tt_QjThYWoLL_Wmy7aLCpEo9y3c,5829
89
89
  opencos/utils/vscode_helper.py,sha256=9nHyMUIL-gzfW-qLH06sgaCnVK-YTOtu6pusitNNhL8,1363
90
90
  opencos/utils/vsim_helper.py,sha256=1johPOGbjbMgnCDSTpgsQcSuAquiqq1Y2MBxS6WY6b4,1552
91
- opencos_eda-0.3.3.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
92
- opencos_eda-0.3.3.dist-info/licenses/LICENSE.spdx,sha256=8gn1610RMP6eFgT3Hm6q9VKXt0RvdTItL_oxMo72jII,189
93
- opencos_eda-0.3.3.dist-info/METADATA,sha256=MGcmalurglc-9-KPz1YHp-KOVgLwqOZRRu3cOz6asI0,703
94
- opencos_eda-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
- opencos_eda-0.3.3.dist-info/entry_points.txt,sha256=6n1T5NwVYDhN5l1h5zmyT197G4pE0SySDreB0QJzJR0,218
96
- opencos_eda-0.3.3.dist-info/top_level.txt,sha256=J4JDP-LpRyJqPNeh9bSjx6yrLz2Mk0h6un6YLmtqql4,8
97
- opencos_eda-0.3.3.dist-info/RECORD,,
91
+ opencos_eda-0.3.6.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
92
+ opencos_eda-0.3.6.dist-info/licenses/LICENSE.spdx,sha256=8gn1610RMP6eFgT3Hm6q9VKXt0RvdTItL_oxMo72jII,189
93
+ opencos_eda-0.3.6.dist-info/METADATA,sha256=EYL2QzXAEnFV7r3iyi2DGm2IBBHNxK8c-seSSIeZIao,1039
94
+ opencos_eda-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
95
+ opencos_eda-0.3.6.dist-info/entry_points.txt,sha256=6n1T5NwVYDhN5l1h5zmyT197G4pE0SySDreB0QJzJR0,218
96
+ opencos_eda-0.3.6.dist-info/top_level.txt,sha256=J4JDP-LpRyJqPNeh9bSjx6yrLz2Mk0h6un6YLmtqql4,8
97
+ opencos_eda-0.3.6.dist-info/RECORD,,