• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nbiotcloud / ucdp / 15810636051

22 Jun 2025 08:45PM UTC coverage: 96.782% (+0.001%) from 96.781%
15810636051

push

github

iccode17
re-gen

4932 of 5096 relevant lines covered (96.78%)

7.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.34
/src/ucdp/cli.py
1
#
2
# MIT License
3
#
4
# Copyright (c) 2024-2025 nbiotcloud
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in all
14
# copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
#
24

25
"""Command Line Interface."""
26

27
import logging
8✔
28
import sys
8✔
29
from collections import defaultdict
8✔
30
from collections.abc import Iterable
8✔
31
from logging import StreamHandler
8✔
32
from pathlib import Path
8✔
33
from typing import Literal
8✔
34

35
import click
8✔
36
from click_bash42_completion import patch
8✔
37
from pydantic import BaseModel, ConfigDict
8✔
38
from rich import box
8✔
39
from rich.console import Console
8✔
40
from rich.logging import RichHandler
8✔
41
from rich.pretty import pprint
8✔
42
from rich.table import Table
8✔
43

44
from ._cligroup import MainGroup
8✔
45
from ._logging import HasErrorHandler
8✔
46
from .cache import CACHE
8✔
47
from .cliutil import (
8✔
48
    PathType,
49
    arg_template_filepaths,
50
    arg_top,
51
    arg_tops,
52
    auto_path,
53
    defines2data,
54
    opt_check,
55
    opt_create,
56
    opt_defines,
57
    opt_dry_run,
58
    opt_file,
59
    opt_filelist,
60
    opt_filepath,
61
    opt_local,
62
    opt_maxlevel,
63
    opt_maxworkers,
64
    opt_path,
65
    opt_show_diff,
66
    opt_tag,
67
    opt_target,
68
    opt_topsfile,
69
    read_file,
70
)
71
from .consts import PATH
8✔
72
from .create import TB_MAP, TYPE_CHOICES, CreateInfo
8✔
73
from .create import create as create_
8✔
74
from .fileset import FileSet
8✔
75
from .finder import find
8✔
76
from .generate import Generator, clean, get_makolator, render_generate, render_inplace
8✔
77
from .iterutil import namefilter
8✔
78
from .loader import load
8✔
79
from .modfilelist import iter_modfilelists
8✔
80
from .modtopref import PAT_TOPMODREF, TopModRef
8✔
81
from .pathutil import relative
8✔
82
from .top import Top
8✔
83
from .util import LOGGER, guess_path
8✔
84

85
patch()
8✔
86

87

88
_LOGLEVELMAP = {
8✔
89
    0: logging.WARNING,
90
    1: logging.INFO,
91
    2: logging.DEBUG,
92
}
93

94

95
class Ctx(BaseModel):
8✔
96
    """Command Line Context."""
97

98
    model_config = ConfigDict(
8✔
99
        arbitrary_types_allowed=True,
100
    )
101

102
    console: Console
8✔
103
    has_error_handler: HasErrorHandler | None = None
8✔
104

105
    verbose: int = 0
8✔
106
    no_cache: bool = False
8✔
107
    no_color: bool | None = None
8✔
108

109
    @staticmethod
8✔
110
    def create(no_color: bool | None = None, **kwargs) -> "Ctx":
8✔
111
        """Create."""
112
        console = Console(log_time=False, log_path=False, no_color=no_color)
8✔
113
        has_error_handler = HasErrorHandler()
8✔
114
        return Ctx(console=console, has_error_handler=has_error_handler, no_color=no_color, **kwargs)
8✔
115

116
    def __enter__(self):
8✔
117
        # Logging
118
        level = _LOGLEVELMAP.get(self.verbose, logging.DEBUG)
8✔
119
        if not self.no_color:
8✔
120
            handler = RichHandler(
8✔
121
                show_time=False,
122
                show_path=False,
123
                rich_tracebacks=True,
124
                console=Console(stderr=True, no_color=self.no_color),
125
            )
126
            format_ = "%(message)s"
8✔
127
        else:
128
            handler = StreamHandler(stream=sys.stderr)
8✔
129
            format_ = "%(levelname)s %(message)s"
8✔
130
        handlers = [handler, self.has_error_handler]
8✔
131
        logging.basicConfig(level=level, format=format_, handlers=handlers)
8✔
132

133
        # Cache
134
        if self.no_cache:
8✔
135
            CACHE.disable()
×
136

137
        return self
8✔
138

139
    def __exit__(self, exc_type, exc_value, tb):
8✔
140
        if exc_type or self.has_error_handler.has_errors:
8✔
141
            if exc_type is KeyboardInterrupt:
8✔
142
                self.console.print("[red]Aborted.")
×
143
            else:
144
                self.console.print("[red][bold]Failed.")
8✔
145
            sys.exit(1)
8✔
146

147

148
@click.group(cls=MainGroup, context_settings={"help_option_names": ["-h", "--help"]})
8✔
149
@click.option("-v", "--verbose", count=True, help="Increase Verbosity.")
8✔
150
@click.option("-C", "--no-cache", is_flag=True, help="Disable Caching.")
8✔
151
@click.option("--no-color", is_flag=True, help="Disable Coloring.", envvar="UCDP_NO_COLOR")
8✔
152
@click.version_option()
8✔
153
@click.pass_context
8✔
154
def ucdp(ctx, verbose=0, no_cache=False, no_color=False):
8✔
155
    """Unified Chip Design Platform."""
156
    ctx.obj = ctx.with_resource(Ctx.create(verbose=verbose, no_cache=no_cache, no_color=no_color))
8✔
157

158

159
pass_ctx = click.make_pass_decorator(Ctx)
8✔
160

161

162
def get_group(help=None):  # pragma: no cover
163
    """Create Command Group."""
164

165
    @click.group(help=help)
166
    @click.pass_context
167
    def group(ctx):
168
        ctx.obj = Ctx(console=Console(log_time=False, log_path=False))
169

170
    return group
171

172

173
def load_top(ctx: Ctx, top: str | TopModRef, paths: Iterable[str | Path], quiet: bool = False) -> Top:
8✔
174
    """Load Top Module."""
175
    lpaths = [Path(path) for path in paths]
8✔
176
    # Check if top seems to be some kind of file path
177
    topmodref = TopModRef.cast(guess_path(top) or top) if isinstance(top, str) else top
8✔
178
    if quiet:
8✔
179
        return load(topmodref, paths=lpaths)
8✔
180
    with ctx.console.status(f"Loading '{topmodref!s}'"):
8✔
181
        result = load(topmodref, paths=lpaths)
8✔
182
    ctx.console.log(f"'{topmodref!s}' checked.")
8✔
183
    return result
8✔
184

185

186
@ucdp.command(
8✔
187
    help=f"""
188
Load Data Model and Check.
189

190
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
191
"""
192
)
193
@arg_top
8✔
194
@opt_path
8✔
195
@click.option("--stat", default=False, is_flag=True, help="Show Statistics.")
8✔
196
@pass_ctx
8✔
197
def check(ctx, top, path, stat=False):
8✔
198
    """Check."""
199
    top = load_top(ctx, top, path)
8✔
200
    if stat:
8✔
201
        print("Statistics:")
8✔
202
        for name, value in top.get_stat().items():
8✔
203
            print(f"  {name}: {value}")
8✔
204

205

206
@ucdp.command(
8✔
207
    help=f"""
208
Load Data Model and Generate Files.
209

210
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
211
"""
212
)
213
@arg_tops
8✔
214
@opt_path
8✔
215
@opt_filelist
8✔
216
@opt_target
8✔
217
@opt_show_diff
8✔
218
@opt_maxworkers
8✔
219
@opt_defines
8✔
220
@opt_local
8✔
221
@opt_topsfile
8✔
222
@opt_check
8✔
223
@opt_create
8✔
224
@pass_ctx
8✔
225
def gen(
8✔
226
    ctx,
227
    tops,
228
    path,
229
    filelist,
230
    target=None,
231
    show_diff=False,
232
    maxworkers=None,
233
    define=None,
234
    local=None,
235
    check=False,
236
    create=False,
237
    tops_file=None,
238
):
239
    """Generate."""
240
    tops = list(tops)
8✔
241
    for filepath in tops_file or []:
8✔
242
        tops.extend(read_file(filepath))
8✔
243
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
8✔
244
    data = defines2data(define)
8✔
245
    filelist = filelist or ["*"]
8✔
246
    with Generator(makolator=makolator, maxworkers=maxworkers, check=check) as generator:
8✔
247
        for info in find(path, patterns=tuple(tops), local=local, is_top=True):
8✔
248
            try:
8✔
249
                top = load_top(ctx, info.topmodref, path)
8✔
250
            except Exception as exc:
×
251
                LOGGER.warning(f"Cannot load '{info.topmodref}'")
×
252
                LOGGER.warning(str(exc))
×
253
                LOGGER.warning(f"Debug with 'ucdp check {info.topmodref}'")
×
254
                continue
×
255
            for item in filelist:
8✔
256
                generator.generate(top, item, target=target, data=data)
8✔
257

258

259
@ucdp.command(
8✔
260
    help=f"""
261
Load Data Model and Render Template and Create File.
262

263
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
264

265
TEMPLATE_FILEPATHS: Templates to render. Environment Variable 'UCDP_TEMPLATE_FILEPATHS'
266
                    Templates in `templates` folders are found automatically.
267

268
GENFILE: Generated File.
269
"""
270
)
271
@arg_top
8✔
272
@opt_path
8✔
273
@arg_template_filepaths
8✔
274
@click.argument("genfile", type=PathType, shell_complete=auto_path, nargs=1)
8✔
275
@opt_show_diff
8✔
276
@opt_defines
8✔
277
@opt_create
8✔
278
@pass_ctx
8✔
279
def rendergen(ctx, top, path, template_filepaths, genfile, show_diff=False, define=None, create=False):
8✔
280
    """Render Generate."""
281
    top = load_top(ctx, top, path)
8✔
282
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
8✔
283
    data = defines2data(define)
8✔
284
    render_generate(top, template_filepaths, genfile=genfile, makolator=makolator, data=data)
8✔
285

286

287
@ucdp.command(
8✔
288
    help=f"""
289
Load Data Model and Render Template and Update File.
290

291
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
292

293
TEMPLATE_FILEPATHS: Templates to render. Environment Variable 'UCDP_TEMPLATE_FILEPATHS'
294
                    Templates in `templates` folders are found automatically.
295

296
INPLACEFILE: Inplace File.
297
"""
298
)
299
@arg_top
8✔
300
@opt_path
8✔
301
@arg_template_filepaths
8✔
302
@click.argument("inplacefile", type=PathType, shell_complete=auto_path, nargs=1)
8✔
303
@opt_show_diff
8✔
304
@opt_defines
8✔
305
@opt_create
8✔
306
@click.option("--ignore_unknown", "-i", default=False, is_flag=True, help="Ignore Unknown Placeholder.")
8✔
307
@pass_ctx
8✔
308
def renderinplace(
8✔
309
    ctx, top, path, template_filepaths, inplacefile, show_diff=False, define=None, ignore_unknown=False, create=False
310
):
311
    """Render Inplace."""
312
    top = load_top(ctx, top, path)
8✔
313
    makolator = get_makolator(show_diff=show_diff, paths=path, create=create)
8✔
314
    data = defines2data(define)
8✔
315
    render_inplace(
8✔
316
        top,
317
        template_filepaths,
318
        inplacefile=inplacefile,
319
        makolator=makolator,
320
        data=data,
321
        ignore_unknown=ignore_unknown,
322
    )
323

324

325
@ucdp.command(
8✔
326
    help=f"""
327
Load Data Model and REMOVE Generated Files.
328

329
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
330
"""
331
)
332
@arg_top
8✔
333
@opt_path
8✔
334
@opt_filelist
8✔
335
@opt_target
8✔
336
@opt_show_diff
8✔
337
@opt_dry_run
8✔
338
@opt_maxworkers
8✔
339
@pass_ctx
8✔
340
def cleangen(ctx, top, path, filelist, target=None, show_diff=False, maxworkers=None, dry_run=False):
8✔
341
    """Clean Generated Files."""
342
    top = load_top(ctx, top, path)
8✔
343
    makolator = get_makolator(show_diff=show_diff, paths=path)
8✔
344
    for item in filelist or ["*"]:
8✔
345
        clean(top, item, target=target, makolator=makolator, maxworkers=maxworkers, dry_run=dry_run)
8✔
346

347

348
@ucdp.command(
8✔
349
    help=f"""
350
Load Data Model and Generate File List.
351

352
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
353
"""
354
)
355
@arg_top
8✔
356
@opt_path
8✔
357
@opt_filelist
8✔
358
@opt_target
8✔
359
@opt_file
8✔
360
@pass_ctx
8✔
361
def filelist(ctx, top, path, filelist, target=None, file=None):
8✔
362
    """File List."""
363
    # Load quiet, otherwise stdout is messed-up
364
    top = load_top(ctx, top, path, quiet=True)
8✔
365
    for item in filelist or ["*"]:
8✔
366
        fileset = FileSet.from_mod(top.mod, item, target=target)
8✔
367
        for line in fileset:
8✔
368
            print(line, file=file)
8✔
369

370

371
@ucdp.command(
8✔
372
    help=f"""
373
Load Data Model and Show File Information
374

375
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
376
"""
377
)
378
@arg_top
8✔
379
@opt_path
8✔
380
@opt_filelist
8✔
381
@opt_target
8✔
382
@opt_maxlevel
8✔
383
@click.option("--minimal", "-m", default=False, is_flag=True, help="Skip defaults.")
8✔
384
@opt_file
8✔
385
@pass_ctx
8✔
386
def fileinfo(ctx, top, path, filelist, target=None, maxlevel=None, minimal=False, file=None):
8✔
387
    """File List."""
388
    # Load quiet, otherwise stdout is messed-up
389
    top = load_top(ctx, top, path, quiet=True)
8✔
390
    console = Console(file=file) if file else ctx.console
8✔
391
    for item in filelist or ["*"]:
8✔
392
        data = defaultdict(list)
8✔
393
        for mod, modfilelist in iter_modfilelists(top.mod, item, target=target, maxlevel=maxlevel):
8✔
394
            data[str(mod)].append(modfilelist.model_dump(exclude_defaults=minimal))
8✔
395
        pprint(dict(data), indent_guides=False, console=console)
8✔
396

397

398
@ucdp.command(
8✔
399
    help="""
400
              List Available Data Models.
401

402
              PATTERN: Limit list to these modules only.
403

404
              Examples:
405

406
                ucdp ls
407

408
                ucdp ls -n
409

410
                ucdp ls glbl_lib*
411
              """
412
)
413
@arg_tops
8✔
414
@opt_path
8✔
415
@click.option("--names", "-n", default=False, is_flag=True, help="Just print names")
8✔
416
@click.option("--top/--no-top", "-t/-T", default=None, is_flag=True, help="List loadable top modules only.")
8✔
417
@click.option("--tb/--no-tb", "-b/-B", default=None, is_flag=True, help="List testbench modules only.")
8✔
418
@click.option("--generic-tb", "-g", default=False, is_flag=True, help="List Generic Testbench modules only.")
8✔
419
@opt_local
8✔
420
@click.option("--base", "-A", default=False, is_flag=True, help="Show Base Classes.")
8✔
421
@click.option("--filepath", "-f", default=False, is_flag=True, help="Show File Path.")
8✔
422
@click.option("--abs-filepath", "-F", default=False, is_flag=True, help="Show Absolute File Path.")
8✔
423
@opt_tag
8✔
424
@pass_ctx
8✔
425
def ls(  # noqa: C901
8✔
426
    ctx,
427
    path=None,
428
    tops=None,
429
    names=False,
430
    top=None,
431
    tb=None,
432
    local=None,
433
    generic_tb=False,
434
    tag=None,
435
    base=False,
436
    filepath=False,
437
    abs_filepath=False,
438
):
439
    """List Modules."""
440
    with ctx.console.status("Searching"):
8✔
441
        infos = find(path, patterns=tops or ["*"], local=local)
8✔
442
    if top is not None:
8✔
443
        infos = [info for info in infos if info.is_top == top]
8✔
444
    if tb is not None:
8✔
445
        infos = [info for info in infos if bool(info.tb) == tb]
8✔
446
    if generic_tb:
8✔
447
        infos = [info for info in infos if info.tb == "Generic"]
8✔
448
    if tag:
8✔
449
        filter_ = namefilter(tag)
8✔
450
        infos = [info for info in infos if any(filter_(tag) for tag in info.tags)]
8✔
451

452
    def fill_row(row, info):
8✔
453
        if base:
8✔
454
            row.append(info.modbasecls.__name__)
8✔
455
        if filepath:
8✔
456
            row.append(str(relative(info.filepath)))
8✔
457
        if abs_filepath:
8✔
458
            row.append(str(info.filepath))
8✔
459

460
    if names:
8✔
461
        for info in infos:
8✔
462
            row = [info.topmodref]
8✔
463
            fill_row(row, info)
8✔
464
            print(*row)
8✔
465
    else:
466
        table = Table(expand=filepath or abs_filepath, box=box.MARKDOWN)
8✔
467
        table.add_column("Reference")
8✔
468
        table.add_column("Top", justify="center")
8✔
469
        table.add_column("Tb ", justify="center")
8✔
470
        table.add_column("Tags")
8✔
471
        if base:
8✔
472
            table.add_column("Bases on", justify="right")
8✔
473
        if filepath:
8✔
474
            table.add_column("Filepath")
×
475
        if abs_filepath:
8✔
476
            table.add_column("Absolute Filepath")
×
477
        for info in infos:
8✔
478
            row = [
8✔
479
                str(info.topmodref),
480
                "X" if info.is_top else "",
481
                "X" if info.tb else "",
482
                ",".join(sorted(info.tags)),
483
            ]
484
            fill_row(row, info)
8✔
485
            table.add_row(*row)
8✔
486
        ctx.console.print(table)
8✔
487

488

489
@ucdp.command(
8✔
490
    help=f"""
491
Load Data Model and Module Information.
492

493
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
494
"""
495
)
496
@arg_tops
8✔
497
@opt_path
8✔
498
@opt_local
8✔
499
@click.option("--top", "-t", default=None, is_flag=True, help="List loadable top modules only.")
8✔
500
@click.option("--sub", "-S", default=False, is_flag=True, help="Show Submodules.")
8✔
501
@pass_ctx
8✔
502
def modinfo(ctx, tops, path, local, top, sub):
8✔
503
    """Module Information."""
504
    sep = ""
8✔
505
    for info in find(path, patterns=tops, local=local, is_top=top):
8✔
506
        try:
8✔
507
            top = load_top(ctx, info.topmodref, path, quiet=True)
8✔
508
        except Exception as exc:
8✔
509
            LOGGER.warning(str(exc))
8✔
510
            continue
8✔
511
        print(sep + top.mod.get_info(sub=sub))
8✔
512
        sep = "\n\n"
8✔
513

514

515
@ucdp.command(
8✔
516
    help=f"""
517
Load Data Model and Show Module Overview.
518

519
TOP: Top Module. {PAT_TOPMODREF}. Environment Variable 'UCDP_TOP'
520
"""
521
)
522
@arg_top
8✔
523
@opt_path
8✔
524
@click.option("--minimal", "-m", default=False, is_flag=True, help="Skip modules without specific details")
8✔
525
@opt_filepath
8✔
526
@opt_tag
8✔
527
@pass_ctx
8✔
528
def overview(ctx, top, path, minimal=False, file=None, tag=None):
8✔
529
    """Overview."""
530
    # Load quiet, otherwise stdout is messed-up
531
    top = load_top(ctx, top, path, quiet=True)
8✔
532
    data = {"minimal": minimal, "tags": tag}
8✔
533
    render_generate(top, [PATH / "ucdp-templates" / "overview.txt.mako"], genfile=file, data=data, no_stat=True)
8✔
534

535

536
@ucdp.group(context_settings={"help_option_names": ["-h", "--help"]})
8✔
537
def info():
8✔
538
    """Information."""
539

540

541
@info.command()
8✔
542
@pass_ctx
8✔
543
def examples(ctx):
8✔
544
    """Path to Examples."""
545
    examples_path = Path(__file__).parent / "examples"
8✔
546
    print(str(examples_path))
8✔
547

548

549
@info.command()
8✔
550
@opt_path
8✔
551
@pass_ctx
8✔
552
def template_paths(ctx, path):
8✔
553
    """Template Paths."""
554
    makolator = get_makolator(paths=path)
8✔
555
    for template_path in makolator.config.template_paths:
8✔
556
        print(str(template_path))
8✔
557

558

559
@ucdp.command(
8✔
560
    help="""
561
Create Datamodel Skeleton.
562
"""
563
)
564
@click.option("--module", "-m", prompt=True, help="Name of the Module")
8✔
565
@click.option("--library", "-l", default=Path().resolve().name, prompt=True, help="Name of the Library")
8✔
566
@click.option("--regf/--no-regf", "-r/-R", default=True, help="Make use of a Register File")
8✔
567
@click.option("--descr", "-d", default="", help="Description")
8✔
568
@click.option("--flavour", "-F", type=click.Choice(TYPE_CHOICES, case_sensitive=False), help="Choose a Module Flavour")
8✔
569
@click.option("--tb/--no-tb", "-t/-T", default=None, help="Create testbench for design module")
8✔
570
@click.option("--force", "-f", is_flag=True, help="Overwrite existing files")
8✔
571
@pass_ctx
8✔
572
def create(
8✔
573
    ctx,
574
    module,
575
    library,
576
    regf,
577
    descr,
578
    flavour,
579
    tb,
580
    force,
581
):
582
    """Let The User Type In The Name And Library Of The File."""
583
    if flavour is None:
8✔
584
        flavour = prompt_flavour()
8✔
585
        ctx.console.print(f"\nYou choose the flavour [bold blue]{flavour}[/bold blue].\n")
8✔
586

587
    info = CreateInfo(module=module, library=library, regf=regf, descr=descr, flavour=flavour)
8✔
588

589
    if info.is_tb:
8✔
590
        if not module.endswith("_tb"):
8✔
591
            LOGGER.warning(f"Your testbench module name {module!r} does not end with '_tb'")
8✔
592

593
    else:
594
        if module.endswith("_tb"):
8✔
595
            LOGGER.warning(f"Your design module name {module!r} ends with '_tb'")
8✔
596
        if tb is None:
8✔
597
            answer = click.prompt(
8✔
598
                "Do you want to create a corresponding testbench? (y)es. (n)o.",
599
                type=click.Choice(["y", "n"]),
600
                default="y",
601
            )
602
            tb = answer == "y"
8✔
603
            ctx.console.print("")
8✔
604
        if tb:
8✔
605
            tbinfo = CreateInfo(module=f"{module}_tb", library=library, regf=regf, descr=descr, flavour=TB_MAP[flavour])
8✔
606
            create_(tbinfo, force)
8✔
607
    create_(info, force)
8✔
608

609

610
Type = Literal["AConfigurableMod", "AConfigurableTbMod", "AGenericTbMod", "AMod", "ATailoredMod", "ATbMod"]
8✔
611

612

613
def prompt_flavour() -> Type:
8✔
614
    """Let The User Choose The Type Of The File."""
615
    answer = click.prompt("Do you want to build a (d)esign or (t)estbench?", type=click.Choice(["d", "t"]), default="d")
8✔
616
    if answer == "d":
8✔
617
        answer = click.prompt(
8✔
618
            "Does your design vary more than what `parameter` can cover? (y)es. (n)o.", type=click.Choice(["y", "n"])
619
        )
620
        if answer == "y":
8✔
621
            answer = click.prompt(
8✔
622
                "Do you want to use a (c)onfig or (t) shall the parent module tailor the functionality?",
623
                type=click.Choice(["c", "t"]),
624
            )
625
            if answer == "c":
8✔
626
                flavour_ = "AConfigurableMod"
8✔
627

628
            else:
629
                flavour_ = "ATailoredMod"
8✔
630

631
        else:
632
            flavour_ = "AMod"
8✔
633

634
    else:
635
        answer = click.prompt(
8✔
636
            "Do you want to build a generic testbench which tests similar modules? (y)es. (n)o.",
637
            type=click.Choice(["y", "n"]),
638
        )
639
        if answer == "y":
8✔
640
            answer = click.prompt(
8✔
641
                "Do you want to automatically adapt your testbench to your (g) dut or use a (c)onfig?",
642
                type=click.Choice(["g", "c"]),
643
            )
644
            if answer == "c":
8✔
645
                flavour_ = "AConfigurableTbMod"
8✔
646

647
            else:
648
                flavour_ = "AGenericTbMod"
8✔
649

650
        else:
651
            flavour_ = "ATbMod"
8✔
652

653
    return flavour_
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc