mooreio-client 2.0.2__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.
Files changed (67) hide show
  1. mio_client/__init__.py +0 -0
  2. mio_client/__main__.py +9 -0
  3. mio_client/cli.py +188 -0
  4. mio_client/commands/__init__.py +0 -0
  5. mio_client/commands/gen.py +262 -0
  6. mio_client/commands/ip.py +599 -0
  7. mio_client/commands/misc.py +210 -0
  8. mio_client/commands/project.py +11 -0
  9. mio_client/commands/sim.py +932 -0
  10. mio_client/commands/team.py +11 -0
  11. mio_client/commands/user.py +133 -0
  12. mio_client/commands/web.py +10 -0
  13. mio_client/core/__init__.py +0 -0
  14. mio_client/core/command.py +868 -0
  15. mio_client/core/configuration.py +130 -0
  16. mio_client/core/ip.py +1146 -0
  17. mio_client/core/model.py +34 -0
  18. mio_client/core/phase.py +160 -0
  19. mio_client/core/root_manager.py +1179 -0
  20. mio_client/core/scheduler.py +374 -0
  21. mio_client/core/service.py +143 -0
  22. mio_client/core/user.py +63 -0
  23. mio_client/core/version.py +86 -0
  24. mio_client/schedulers/__init__.py +0 -0
  25. mio_client/schedulers/grid_engine.py +32 -0
  26. mio_client/schedulers/lsf.py +31 -0
  27. mio_client/schedulers/sub_process.py +65 -0
  28. mio_client/services/__init__.py +0 -0
  29. mio_client/services/cdc.py +14 -0
  30. mio_client/services/dft.py +16 -0
  31. mio_client/services/doxygen.py +117 -0
  32. mio_client/services/emulation.py +14 -0
  33. mio_client/services/formal.py +16 -0
  34. mio_client/services/fsoc.py +15 -0
  35. mio_client/services/init.py +212 -0
  36. mio_client/services/layout.py +15 -0
  37. mio_client/services/lint.py +16 -0
  38. mio_client/services/pv.py +16 -0
  39. mio_client/services/regression.py +862 -0
  40. mio_client/services/simulation.py +2369 -0
  41. mio_client/services/spice.py +16 -0
  42. mio_client/services/sta.py +16 -0
  43. mio_client/services/synthesis.py +16 -0
  44. mio_client/services/uvmf.py +16 -0
  45. mio_client/services/uvmx.py +16 -0
  46. mio_client/tests/__init__.py +0 -0
  47. mio_client/tests/common.py +253 -0
  48. mio_client/tests/conftest.py +10 -0
  49. mio_client/tests/test_cli_dox.py +52 -0
  50. mio_client/tests/test_cli_init.py +102 -0
  51. mio_client/tests/test_cli_ip.py +133 -0
  52. mio_client/tests/test_cli_misc.py +161 -0
  53. mio_client/tests/test_cli_regr.py +134 -0
  54. mio_client/tests/test_cli_sim.py +167 -0
  55. mio_client/tests/test_cli_user.py +28 -0
  56. mio_client/tests/test_configuration.py +86 -0
  57. mio_client/tests/test_core.py +258 -0
  58. mio_client/tests/test_init.py +63 -0
  59. mio_client/tests/test_ip.py +106 -0
  60. mio_client/tests/test_ts.py +141 -0
  61. mio_client/tests/test_user.py +54 -0
  62. mooreio_client-2.0.2.dist-info/LICENSE +21 -0
  63. mooreio_client-2.0.2.dist-info/METADATA +209 -0
  64. mooreio_client-2.0.2.dist-info/RECORD +67 -0
  65. mooreio_client-2.0.2.dist-info/WHEEL +5 -0
  66. mooreio_client-2.0.2.dist-info/entry_points.txt +2 -0
  67. mooreio_client-2.0.2.dist-info/top_level.txt +1 -0
mio_client/__init__.py ADDED
File without changes
mio_client/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ # Copyright 2020-2024 Datum Technology Corporation
2
+ # All rights reserved.
3
+ #######################################################################################################################
4
+ import sys
5
+
6
+ from mio_client.cli import main
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(main(sys.argv[1:]))
mio_client/cli.py ADDED
@@ -0,0 +1,188 @@
1
+ # Copyright 2020-2024 Datum Technology Corporation
2
+ # All rights reserved.
3
+ #######################################################################################################################
4
+ import argparse
5
+ import pathlib
6
+ import sys
7
+ import os
8
+
9
+ from mio_client.commands import sim, ip, misc, project, team, user, web, gen
10
+ from mio_client.core.root_manager import RootManager
11
+
12
+ #######################################################################################################################
13
+ # User Manual Top
14
+ #######################################################################################################################
15
+ VERSION = "2.0.2"
16
+
17
+ HELP_TEXT = f"""
18
+ Moore.io (`mio`) Client - v{VERSION}
19
+ User Manual: https://mio-client.readthedocs.io/
20
+ https://mooreio.com - Copyright 2020-2024 Datum Technology Corporation - https://datumtc.ca
21
+ Usage:
22
+ mio [--version] [--help]
23
+ mio [--wd WD] [--dbg] CMD [OPTIONS]
24
+
25
+ Options:
26
+ -v, --version
27
+ Prints the mio version and exits.
28
+
29
+ -h, --help
30
+ Prints the overall synopsis and a list of the most commonly used commands and exits.
31
+
32
+ -C WD, --wd WD
33
+ Run as if mio was started in WD (Working Directory) instead of the Present Working Directory `pwd`.
34
+
35
+ --dbg
36
+ Enables tracing outputs from mio.
37
+
38
+ Full Command List (`mio help CMD` for help on a specific command):
39
+ Help and Shell/Editor Integration
40
+ help Prints documentation for mio commands
41
+
42
+ Project and Code Management
43
+ init Creates essential files necessary for new Projects/IPs
44
+
45
+ IP and Credentials Management
46
+ install Installs all IP dependencies from IP Marketplace
47
+ login Starts session with IP Marketplace
48
+ package Creates a compressed (and encrypted) archive of an IP
49
+ publish Publishes IP to Server (must have mio admin account)
50
+
51
+ EDA Automation
52
+ clean Delete IP EDA artifacts and/or Moore.io Project directory contents (.mio)
53
+ sim Performs necessary steps to simulate an IP with any simulator
54
+ regr Runs regression against an IP
55
+ dox Generates source reference documentation with Doxygen"""
56
+
57
+
58
+ #######################################################################################################################
59
+ # Main
60
+ #######################################################################################################################
61
+ URL_BASE = 'https://mooreio.com'
62
+ URL_AUTHENTICATION = f'{URL_BASE}/auth/token'
63
+ TEST_MODE = False
64
+ USER_HOME_PATH = pathlib.Path(os.path.expanduser("~/.mio"))
65
+ root_manager: RootManager
66
+ def main(args=None) -> int:
67
+ """
68
+ Main entry point. Performs the following steps in order:
69
+ - Create CLI argument parser
70
+ - Find all commands and register them
71
+ - Parse CLI arguments
72
+ - Find the command which matches the parsed arguments
73
+ - Create the Root instance
74
+ - Run the command via the Root instance
75
+ :return: Exit code
76
+ """
77
+ global root_manager
78
+
79
+ try:
80
+ parser = create_top_level_parser()
81
+ subparsers = parser.add_subparsers(dest='command', help='Sub-command help')
82
+ commands = register_all_commands(subparsers)
83
+ args = parser.parse_args(args)
84
+ except Exception as e:
85
+ print(f"Error during parsing of CLI arguments: {e}", file=sys.stderr)
86
+ return 1
87
+
88
+ if args.version:
89
+ print_version_text()
90
+ return 0
91
+ if (not args.command) or args.help:
92
+ print_help_text()
93
+ return 0
94
+
95
+ command = next(
96
+ (
97
+ cmd for cmd in commands
98
+ if cmd.name().lower() == args.command
99
+ ),
100
+ None
101
+ )
102
+ if not command:
103
+ print(f"Unknown command '{args.command}' specified.", file=sys.stderr)
104
+ return 1
105
+
106
+ wd = None
107
+ if args.wd is None:
108
+ wd = pathlib.Path.cwd()
109
+ else:
110
+ try:
111
+ wd = pathlib.Path(args.wd).resolve()
112
+ except Exception as e:
113
+ print(f"Invalid path '{wd}' provided as working directory: {e}", file=sys.stderr)
114
+ return 1
115
+
116
+ root_manager = RootManager("Moore.io Client Root Manager", wd, URL_BASE, URL_AUTHENTICATION, TEST_MODE, USER_HOME_PATH)
117
+ command.parsed_cli_arguments = args
118
+
119
+ if args.dbg:
120
+ root_manager.print_trace = True
121
+
122
+ return root_manager.run(command)
123
+
124
+
125
+ #######################################################################################################################
126
+ # Helper functions
127
+ #######################################################################################################################
128
+ def create_top_level_parser():
129
+ """
130
+ Creates a top-level CLI argument parser.
131
+ :return: argparse.ArgumentParser object representing the top-level parser
132
+ """
133
+ parser = argparse.ArgumentParser(prog="mio", description="", add_help=False)
134
+ parser.add_argument("-h" , "--help" , help="Shows this help message and exits.", action="store_true", default=False, required=False)
135
+ parser.add_argument("-v" , "--version", help="Prints version and exit." , action="store_true", default=False, required=False)
136
+ parser.add_argument("--dbg", help="Enable tracing output." , action="store_true", default=False, required=False)
137
+ parser.add_argument("-C" , "--wd" , help="Run as if mio was started in <path> instead of the current working directory.", type=pathlib.Path, required=False)
138
+ return parser
139
+
140
+ def register_all_commands(subparsers):
141
+ """
142
+ Register all commands to the subparsers.
143
+ :param subparsers: An instance of argparse.ArgumentParser that contains the subparsers.
144
+ :return: A list of registered commands.
145
+ """
146
+ commands = []
147
+ register_commands(commands, sim.get_commands())
148
+ register_commands(commands, ip.get_commands())
149
+ register_commands(commands, misc.get_commands())
150
+ register_commands(commands, project.get_commands())
151
+ register_commands(commands, team.get_commands())
152
+ register_commands(commands, user.get_commands())
153
+ register_commands(commands, web.get_commands())
154
+ register_commands(commands, gen.get_commands())
155
+ for command in commands:
156
+ command.add_to_subparsers(subparsers)
157
+ return commands
158
+
159
+ def register_commands(existing_commands, new_commands):
160
+ """
161
+ Registers new commands into an existing list of commands.
162
+ :param existing_commands: A list of existing commands.
163
+ :param new_commands: A list of new commands to be registered.
164
+ :return: None
165
+ Raises:
166
+ ValueError: If a command name in `new_commands` is already registered in `existing_commands`.
167
+
168
+ """
169
+ new_command_names = {command.name for command in new_commands}
170
+ existing_command_names = {command.name for command in existing_commands}
171
+ for command in new_commands:
172
+ if command.name not in existing_command_names:
173
+ existing_commands.append(command)
174
+ else:
175
+ raise ValueError(f"Command '{command}' is already registered.")
176
+
177
+ def print_help_text():
178
+ print(HELP_TEXT)
179
+
180
+ def print_version_text():
181
+ print(f"Moore.io Client v{VERSION}")
182
+
183
+
184
+ #######################################################################################################################
185
+ # Entry point
186
+ #######################################################################################################################
187
+ if __name__ == "__main__":
188
+ sys.exit(main(sys.argv[1:]))
File without changes
@@ -0,0 +1,262 @@
1
+ # Copyright 2020-2024 Datum Technology Corporation
2
+ # All rights reserved.
3
+ #######################################################################################################################
4
+ from pathlib import Path
5
+ from typing import Dict, List
6
+
7
+ from ..services.init import InitServiceModes, InitServiceReport, InitService, InitProjectConfiguration, \
8
+ InitIpConfiguration
9
+ from ..core.service import ServiceType
10
+ from ..core.phase import Phase
11
+ from ..core.command import Command
12
+ from ..core.ip import Ip, IpPkgType, DutType
13
+
14
+
15
+ #######################################################################################################################
16
+ # Init Command
17
+ #######################################################################################################################
18
+ INIT_HELP_TEXT = """Moore.io Initialization Command
19
+ Creates a new Project skeleton if not already within a Project. If not, a new IP skeleton is created.
20
+ This is the recommended method for importing code to the Moore.io ecosystem.
21
+
22
+ Usage:
23
+ mio init [OPTIONS]
24
+
25
+ Options:
26
+ -i, --input-file # Specifies YAML input file path (instead of prompting user)
27
+
28
+ Examples:
29
+ mio init # Create a new empty Project/IP in this location.
30
+ mio init -i ~/answers.yml # Create a new empty Project/IP in this location with pre-filled data.
31
+ mio -C ~/my_proj init # Create a new empty Project at a specific location."""
32
+
33
+
34
+ def get_commands():
35
+ return [InitCommand]
36
+
37
+
38
+ class InitCommand(Command):
39
+ def __init__(self):
40
+ super().__init__()
41
+ self._prompt_user: bool = False
42
+ self._user_input_file: Path = Path()
43
+ self._mode: InitServiceModes = InitServiceModes.UNDEFINED
44
+ self._init_project_configuration: InitProjectConfiguration
45
+ self._init_ip_configuration: InitIpConfiguration
46
+ self._init_service: InitService
47
+ self._report: InitServiceReport
48
+ self._success: bool = False
49
+
50
+ @staticmethod
51
+ def name() -> str:
52
+ return "init"
53
+
54
+ @property
55
+ def prompt_user(self) -> bool:
56
+ return self._prompt_user
57
+
58
+ @property
59
+ def user_input_file(self) -> Path:
60
+ return self._user_input_file
61
+
62
+ @property
63
+ def mode(self) -> InitServiceModes:
64
+ return self._mode
65
+
66
+ @property
67
+ def init_project_configuration(self) -> InitProjectConfiguration:
68
+ return self._init_project_configuration
69
+
70
+ @property
71
+ def init_ip_configuration(self) -> InitIpConfiguration:
72
+ return self._init_ip_configuration
73
+
74
+ @property
75
+ def init_service(self) -> InitService:
76
+ return self._init_service
77
+
78
+ @property
79
+ def report(self) -> InitServiceReport:
80
+ return self._report
81
+
82
+ @property
83
+ def success(self) -> bool:
84
+ return self._success
85
+
86
+ @staticmethod
87
+ def add_to_subparsers(subparsers):
88
+ parser_init = subparsers.add_parser('init', help=INIT_HELP_TEXT, add_help=False)
89
+ parser_init.add_argument('-i', "--input-file", help='Specifies YAML input file path (instead of prompting user)', type=str, required=False)
90
+
91
+ def needs_authentication(self) -> bool:
92
+ return False
93
+
94
+ def phase_init(self, phase: Phase):
95
+ if self.parsed_cli_arguments.input_file:
96
+ self._user_input_file = Path(self.parsed_cli_arguments.input_file.strip())
97
+ if not self.rmh.file_exists(self.user_input_file):
98
+ phase.error = Exception(f"File '{self.user_input_file}' does not exist.")
99
+ else:
100
+ self._prompt_user = False
101
+
102
+ def phase_pre_locate_project_file(self, phase: Phase):
103
+ project_path: Path = self.rmh.locate_project_file()
104
+ if project_path:
105
+ self._mode = InitServiceModes.NEW_IP
106
+ else:
107
+ self._mode = InitServiceModes.NEW_PROJECT
108
+ self._init_service: InitService = InitService(self.rmh)
109
+ try:
110
+ self.fill_project_configuration_from_user_input()
111
+ self.init_project_configuration.input_path = self.rmh.wd
112
+ except Exception as e:
113
+ self._success = False
114
+ phase.error = Exception(f"Failed to obtain valid Project data from user: {e}")
115
+ else:
116
+ try:
117
+ self._report = self.init_service.init_project(self.init_project_configuration)
118
+ except Exception as e:
119
+ phase.error = Exception(f"Failed to initialize Project: {e}")
120
+ self._success = False
121
+ else:
122
+ self._success = self.report.success
123
+
124
+ def phase_pre_ip_discovery(self, phase: Phase):
125
+ if self.mode == InitServiceModes.NEW_IP:
126
+ try:
127
+ self._init_service = self.rmh.service_database.find_service(ServiceType.CODE_GENERATION, "init")
128
+ except Exception as e:
129
+ phase.error = e
130
+ else:
131
+ try:
132
+ self.fill_ip_configuration_from_user_input()
133
+ self.init_ip_configuration.input_path = self.rmh.wd
134
+ except Exception as e:
135
+ self._success = False
136
+ phase.error = Exception(f"Failed to obtain valid IP data from user: {e}")
137
+ else:
138
+ try:
139
+ self._report = self.init_service.init_ip(self.init_ip_configuration)
140
+ except Exception as e:
141
+ phase.error = Exception(f"Failed to initialize IP: {e}")
142
+ self._success = False
143
+ else:
144
+ self._success = self.report.success
145
+ else:
146
+ self.print_report(phase)
147
+ phase.end_process = True
148
+
149
+ def phase_report(self, phase: Phase):
150
+ self.print_report(phase)
151
+
152
+ def print_report(self, phase: Phase):
153
+ if self.success:
154
+ banner = f"{'*' * 53}\033[32m SUCCESS \033[0m{'*' * 54}"
155
+ else:
156
+ banner = f"{'*' * 53}\033[31m\033[4m FAILURE \033[0m{'*' * 54}"
157
+ print(banner)
158
+ if self.mode == InitServiceModes.NEW_PROJECT:
159
+ print(f"New Project '{self.report.name}' initialized at '{self.rmh.wd}'")
160
+ else:
161
+ print(f"New IP '{self.report.name}' initialized at '{self.rmh.wd}'")
162
+ print(banner)
163
+
164
+ def fill_project_configuration_from_user_input(self):
165
+ if self.prompt_user:
166
+ name: str = input("Enter the project name: ").strip()
167
+ full_name: str = input("Enter the project full name: ").strip()
168
+ ip_directories_raw: List[str] = input("Enter the name of directories where Project IP are located (separated by commas): ").split(",")
169
+ ip_directories: List[str] = []
170
+ for ip_directory in ip_directories_raw:
171
+ ip_directories.append(ip_directory.strip())
172
+ sim_directory: str = input("Enter the logic simulation directory: ").strip()
173
+ docs_directory: str = input("Enter the documentation directory: ").strip()
174
+ self._project_configuration: InitProjectConfiguration = InitProjectConfiguration(
175
+ input_path=str(self.rmh.wd),
176
+ name=name.strip().lower(),
177
+ full_name=full_name.strip(),
178
+ ip_directories=ip_directories,
179
+ sim_directory=sim_directory,
180
+ docs_directory=docs_directory
181
+ )
182
+ else:
183
+ self._init_project_configuration = InitProjectConfiguration.load_from_yaml(self.user_input_file)
184
+
185
+ def fill_ip_configuration_from_user_input(self):
186
+ if self.prompt_user:
187
+ ip_types_options = [member.name for member in IpPkgType]
188
+ ip_types_str: str = ', '.join(ip_types_options).lower()
189
+ ip_dut_types_options = [member.name for member in DutType]
190
+ ip_dut_types_str: str = ', '.join(ip_dut_types_options).lower()
191
+ vendor: str = input("Enter the IP vendor: ").strip().lower()
192
+ name: str = input("Enter the IP name: ").strip().lower()
193
+ full_name: str = input("Enter the full IP name: ").strip()
194
+ version: str = input("Enter the IP version (e.g., 1.0.0): ").strip()
195
+ ip_type: Ip.IpType = Ip.IpType[input(f"Enter the IP type [{ip_types_str}]: ").strip().upper()]
196
+ has_docs_directory: bool = bool(input("Does the IP have a documentation directory? (True/False): "))
197
+ docs_directory: str = ""
198
+ if has_docs_directory:
199
+ docs_directory = input("Enter the documentation directory (path): ").strip()
200
+ has_scripts_directory: bool = bool(input("Does the IP have a scripts directory? (True/False): "))
201
+ scripts_directory: str = ""
202
+ if has_scripts_directory:
203
+ scripts_directory = input("Enter the scripts directory (path): ").strip()
204
+ has_examples_directory: bool = bool(input("Does the IP have examples directory? (True/False): "))
205
+ examples_directory: str = ""
206
+ if has_examples_directory:
207
+ examples_directory = input("Enter the examples directory (path): ").strip()
208
+ hdl_src_directory = input("Enter the HDL source directory path: ").strip()
209
+ hdl_src_sub_directories: List[str] = []
210
+ if hdl_src_directory != ".":
211
+ hdl_src_sub_directories_raw: List[str] = input("Enter source sub-directories, separated by commas: ").split(",")
212
+ for hdl_src_sub_directory in hdl_src_sub_directories_raw:
213
+ hdl_src_sub_directories.append(hdl_src_sub_directory.strip())
214
+ hdl_top_sv_files_raw: List[str] = input("Enter the top SystemVerilog file(s), separated by commas: ").split(",")
215
+ hdl_top_sv_files: List[str] = []
216
+ for hdl_top_sv_file in hdl_top_sv_files_raw:
217
+ hdl_top_sv_files.append(hdl_top_sv_file.strip())
218
+ hdl_top_vhdl_files_raw: List[str] = input("Enter the top VHDL file(s), separated by commas: ").split(",")
219
+ hdl_top_vhdl_files: List[str] = []
220
+ for hdl_top_vhdl_file in hdl_top_vhdl_files_raw:
221
+ hdl_top_vhdl_files.append(hdl_top_vhdl_file.strip())
222
+ dut_type: DutType = DutType.MIO_IP
223
+ dut_name: str = ""
224
+ dut_version: str = ""
225
+ hdl_top: List[str] = []
226
+ hdl_tests_path: str = ""
227
+ hdl_tests_name_template: str = ""
228
+ if ip_type == IpPkgType.DV_TB:
229
+ dut_type = DutType[input(f"Enter the DUT type [{ip_dut_types_str}]: ").strip().upper()]
230
+ dut_name = input("Enter the DUT name: ").strip().lower()
231
+ dut_version = input("Enter the DUT version (e.g., 1.0.0): ").strip()
232
+ hdl_top_raw: List[str] = input("Enter the top design construct(s), separated by commas: ").split(",")
233
+ for top in hdl_top_raw:
234
+ hdl_top.append(top.strip())
235
+ hdl_tests_path = input("Enter the tests directory (path): ").strip()
236
+ hdl_tests_name_template = input(f"Enter the template for test class names (e.g., '{name}_{{{{ name }}}}_test_c' ): ").strip()
237
+ self._init_ip_configuration = InitIpConfiguration(
238
+ input_path=str(self.rmh.wd),
239
+ vendor=vendor,
240
+ name=name,
241
+ full_name=full_name,
242
+ version=version,
243
+ ip_type=ip_type,
244
+ has_docs_directory=has_docs_directory,
245
+ docs_directory=docs_directory,
246
+ has_scripts_directory=has_scripts_directory,
247
+ scripts_directory=scripts_directory,
248
+ has_examples_directory=has_examples_directory,
249
+ examples_directory=examples_directory,
250
+ dut_type=dut_type,
251
+ dut_name=dut_name,
252
+ dut_version=dut_version,
253
+ hdl_src_directory=hdl_src_directory,
254
+ hdl_src_sub_directories=hdl_src_sub_directories,
255
+ hdl_top_sv_files=hdl_top_sv_files,
256
+ hdl_top_vhdl_files=hdl_top_vhdl_files,
257
+ hdl_top=hdl_top,
258
+ hdl_tests_path=hdl_tests_path,
259
+ hdl_tests_name_template=hdl_tests_name_template
260
+ )
261
+ else:
262
+ self._init_ip_configuration = InitIpConfiguration.load_from_yaml(self.user_input_file)