| # Copyright lowRISC contributors. | |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| import logging as log | |
| import sys | |
| try: | |
| import enlighten | |
| ENLIGHTEN_EXISTS = True | |
| except ImportError: | |
| ENLIGHTEN_EXISTS = False | |
| class StatusPrinter: | |
| '''Abstraction for printing the current target status onto the console. | |
| Targets are ASIC tool flow steps such as build, run, cov etc. These steps | |
| are sequenced by the Scheduler. There may be multiple jobs running in | |
| parallel in each target. This class provides a mechanism to peridically | |
| print the completion status of each target onto the terminal. Messages | |
| printed by this class are rather static in nature - all the necessary | |
| computations of how the jobs are progressing need to be handled externally. | |
| The following are the 'fields' accepted by this class: | |
| hms: Elapsed time in hh:mm:ss. | |
| target: The tool flow step. | |
| msg: The completion status message (set externally). | |
| perc: Percentage of completion. | |
| ''' | |
| # Print elapsed time in bold. | |
| hms_fmt = ''.join(['\033[1m', u'{hms:9s}', '\033[0m']) | |
| header_fmt = hms_fmt + u' [{target:^13s}]: [{msg}]' | |
| status_fmt = header_fmt + u' {perc:3.0f}%' | |
| def __init__(self): | |
| # Once a target is complete, we no longer need to update it - we can | |
| # just skip it. Maintaining this here provides a way to print the status | |
| # one last time when it reaches 100%. It is much easier to do that here | |
| # than in the Scheduler class. | |
| self.target_done = {} | |
| def print_header(self, msg): | |
| '''Initilize / print the header bar. | |
| The header bar contains an introductory message such as the legend of | |
| what Q, D, ... mean.''' | |
| log.info(self.header_fmt.format(hms="", target="legend", msg=msg)) | |
| def init_target(self, target, msg): | |
| '''Initialize the status bar for each target.''' | |
| self.target_done[target] = False | |
| def update_target(self, target, hms, msg, perc): | |
| '''Periodically update the status bar for each target.''' | |
| if self.target_done[target]: | |
| return | |
| log.info( | |
| self.status_fmt.format(hms=hms, target=target, msg=msg, perc=perc)) | |
| if perc == 100: | |
| self.target_done[target] = True | |
| def exit(self): | |
| '''Do cleanup activities before exitting.''' | |
| pass | |
| class EnlightenStatusPrinter(StatusPrinter): | |
| '''Abstraction for printing status using Enlighten. | |
| Enlighten is a third party progress bar tool. Documentation: | |
| https://python-enlighten.readthedocs.io/en/stable/ | |
| Though it offers very fancy progress bar visualization, we stick to a | |
| simple status bar 'pinned' to the bottom of the screen for each target | |
| that displays statically, a pre-prepared message. We avoid the progress bar | |
| visualization since it requires enlighten to perform some computations the | |
| Scheduler already does. It also helps keep the overhead to a minimum. | |
| Enlighten does not work if the output of dvsim is redirected to a file, for | |
| example - it needs to be attached to a TTY enabled stream. | |
| ''' | |
| def __init__(self): | |
| super().__init__() | |
| # Initialize the status_bars for header and the targets . | |
| self.manager = enlighten.get_manager() | |
| self.status_header = None | |
| self.status_target = {} | |
| def print_header(self, msg): | |
| self.status_header = self.manager.status_bar( | |
| status_format=self.header_fmt, | |
| hms="", | |
| target="legend", | |
| msg= | |
| "Q: queued, D: dispatched, P: passed, F: failed, K: killed, T: total" | |
| ) | |
| def init_target(self, target, msg): | |
| super().init_target(target, msg) | |
| self.status_target[target] = self.manager.status_bar( | |
| status_format=self.status_fmt, | |
| hms="", | |
| target=target, | |
| msg=msg, | |
| perc=0.0) | |
| def update_target(self, target, hms, msg, perc): | |
| if self.target_done[target]: | |
| return | |
| self.status_target[target].update(hms=hms, msg=msg, perc=perc) | |
| if perc == 100: | |
| self.target_done[target] = True | |
| def exit(self): | |
| self.status_header.close() | |
| for target in self.status_target: | |
| self.status_target[target].close() | |
| def get_status_printer(): | |
| """Factory method that returns a status printer instance. | |
| If ENLIGHTEN_EXISTS (enlighten is installed) and stdout is a TTY, then | |
| return an instance of EnlightenStatusPrinter, else return an instance of | |
| StatusPrinter. | |
| """ | |
| if ENLIGHTEN_EXISTS and sys.stdout.isatty(): | |
| return EnlightenStatusPrinter() | |
| else: | |
| return StatusPrinter() |