Skip to content

Simulation

BansheeSimulation

Bases: Simulation

A simulation running on Banshee.

The return code of the simulation is returned directly as the return code of the command launching the simulation.

Source code in util/sim/Simulation.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
class BansheeSimulation(Simulation):
    """A simulation running on Banshee.

    The return code of the simulation is returned directly as the
    return code of the command launching the simulation.
    """

    def __init__(self, banshee_cfg=None, **kwargs):
        """Constructor for the BansheeSimulation class.

        Arguments:
            banshee_cfg: A Banshee config file.
            kwargs: Arguments passed to the base class constructor.
        """
        super().__init__(**kwargs)
        self.cmd = ['banshee', '--no-opt-llvm', '--no-opt-jit', '--configuration',
                    str(banshee_cfg), str(self.elf)]

__init__(banshee_cfg=None, **kwargs)

Constructor for the BansheeSimulation class.

Parameters:

Name Type Description Default
banshee_cfg

A Banshee config file.

None
kwargs

Arguments passed to the base class constructor.

{}
Source code in util/sim/Simulation.py
292
293
294
295
296
297
298
299
300
301
def __init__(self, banshee_cfg=None, **kwargs):
    """Constructor for the BansheeSimulation class.

    Arguments:
        banshee_cfg: A Banshee config file.
        kwargs: Arguments passed to the base class constructor.
    """
    super().__init__(**kwargs)
    self.cmd = ['banshee', '--no-opt-llvm', '--no-opt-jit', '--configuration',
                str(banshee_cfg), str(self.elf)]

QuestaSimulation

Bases: QuestaVCSSimulation

An RTL simulation running on QuestaSim.

Source code in util/sim/Simulation.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
class QuestaSimulation(QuestaVCSSimulation):
    """An RTL simulation running on QuestaSim."""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def get_cpu_time(self):
        # Extract the CPU time from the simulation log
        if self.log is not None:
            with open(self.log, 'r') as f:
                for line in f.readlines():
                    regex = r'Elapsed time: (\d+):(\d+):(\d+)'
                    match = re.search(regex, line)
                    if match:
                        hours = int(match.group(1))
                        minutes = int(match.group(2))
                        seconds = int(match.group(3))
                        return hours*3600 + minutes*60 + seconds

QuestaVCSSimulation

Bases: RTLSimulation

An RTL simulation running on QuestaSim or VCS.

QuestaSim and VCS print out the simulation return code in the simulation log. This must be parsed to extract the return code.

If the simulation is launched through a custom command which implements external verification logic, the return code of the command is used to determine the exit status of the simulation.

Source code in util/sim/Simulation.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
class QuestaVCSSimulation(RTLSimulation):
    """An RTL simulation running on QuestaSim or VCS.

    QuestaSim and VCS print out the simulation return code in the
    simulation log. This must be parsed to extract the return code.

    If the simulation is launched through a custom command which
    implements external verification logic, the return code of the
    command is used to determine the exit status of the simulation.
    """

    def get_retcode(self):
        if self.ext_verif_logic:
            return super().get_retcode()
        elif self.log is not None:
            # Extract the application's return code from the simulation log
            if not self.dry_run:
                with open(self.log, 'r') as f:
                    for line in f.readlines():
                        regex_success = r'\[SUCCESS\] Program finished successfully'
                        match_success = re.search(regex_success, line)
                        if match_success:
                            return 0
                        else:
                            regex_fail = r'\[FAILURE\] Finished with exit code\s+(\d+)'
                            match = re.search(regex_fail, line)
                            if match:
                                return int(match.group(1))
            else:
                return 0

    def successful(self):
        # Check that simulation return code matches expected value (in super class)
        success = super().successful()
        # If not launched through a custom command, check that the simulator process also
        # terminated correctly
        if not self.ext_verif_logic:
            if self.process is None or self.process.returncode != 0:
                return False
        return success

    def get_simulation_time(self):
        # Extract the simulation time from the simulation log
        if self.log is not None:
            with open(self.log, 'r') as f:
                # Read lines from the bottom of the file, since warning and error messages may
                # also print a time to the log.
                for line in reversed(f.readlines()):
                    regex = r'Time: (\d+) ([a-z]+)\s+'
                    match = re.search(regex, line)
                    if match:
                        val = int(match.group(1))
                        unit = match.group(2)
                        if unit == 'ns':
                            return val
                        elif unit == 'us':
                            return val * 1000
                        elif unit == 'ps':
                            return val / 1000
                        else:
                            raise ValueError(f'Unsupported time unit {unit}')

RTLSimulation

Bases: Simulation

A simulation run on an RTL simulator.

An RTL simulation is launched through a simulation binary built in advance from some RTL design. The path to the simulation binary is all that is needed to launch a simulation.

Alternatively, a custom command can be specified to launch the simulation. The custom command generally invokes the RTL simulator binary behind the scenes and executes some additional verification logic at the end of the simulation. As a custom command can implement any verification logic, simulations launched through a custom command are considered unsuccessful if the return code of the custom command is non-null. The custom command may use Mako templating syntax. See the __init__ method implementation for more details on the dynamic arguments which can be used in the command template.

Source code in util/sim/Simulation.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
class RTLSimulation(Simulation):
    """A simulation run on an RTL simulator.

    An RTL simulation is launched through a simulation binary built
    in advance from some RTL design. The path to the simulation binary
    is all that is needed to launch a simulation.

    Alternatively, a custom command can be specified to launch the
    simulation. The custom command generally invokes the RTL simulator
    binary behind the scenes and executes some additional verification
    logic at the end of the simulation. As a custom command can implement
    any verification logic, simulations launched through a custom command
    are considered unsuccessful if the return code of the custom command
    is non-null. The custom command may use Mako templating syntax. See
    the `__init__` method implementation for more details on the dynamic
    arguments which can be used in the command template.
    """

    def __init__(self, sim_bin=None, cmd=None, **kwargs):
        """Constructor for the RTLSimulation class.

        Arguments:
            sim_bin: The simulation binary.
            kwargs: Arguments passed to the base class constructor.
        """
        super().__init__(**kwargs)
        if cmd is None:
            self.cmd = [str(sim_bin), str(self.elf)]
            self.ext_verif_logic = False
        else:
            self.dynamic_args = {
                'sim_bin': str(sim_bin),
                'elf': str(self.elf),
                'run_dir': str(self.run_dir)
            }
            self.cmd = [Template(arg).render(**self.dynamic_args) for arg in cmd]
            self.ext_verif_logic = True

__init__(sim_bin=None, cmd=None, **kwargs)

Constructor for the RTLSimulation class.

Parameters:

Name Type Description Default
sim_bin

The simulation binary.

None
kwargs

Arguments passed to the base class constructor.

{}
Source code in util/sim/Simulation.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def __init__(self, sim_bin=None, cmd=None, **kwargs):
    """Constructor for the RTLSimulation class.

    Arguments:
        sim_bin: The simulation binary.
        kwargs: Arguments passed to the base class constructor.
    """
    super().__init__(**kwargs)
    if cmd is None:
        self.cmd = [str(sim_bin), str(self.elf)]
        self.ext_verif_logic = False
    else:
        self.dynamic_args = {
            'sim_bin': str(sim_bin),
            'elf': str(self.elf),
            'run_dir': str(self.run_dir)
        }
        self.cmd = [Template(arg).render(**self.dynamic_args) for arg in cmd]
        self.ext_verif_logic = True

Simulation

Bases: object

Provides a common interface to manage simulations.

Source code in util/sim/Simulation.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class Simulation(object):
    """Provides a common interface to manage simulations."""

    LOG_FILE = 'sim.txt'

    def __init__(self, elf=None, dry_run=False, retcode=0, run_dir=None, name=None):
        """Constructor for the Simulation class.

        A Simulation object is defined at a minimum by a software
        binary to be simulated on the desired hardware. The hardware is
        implicitly determined by the simulation command.

        Arguments:
            elf: The software binary to simulate.
            run_dir: The directory where to launch the simulation
                command. If none is passed, the current working
                directory is assumed.
            dry_run: A preview of the simulation command will be
                displayed without actually launching the simulation.
        """
        self.elf = elf
        self.dry_run = dry_run
        self.run_dir = run_dir if run_dir is not None else Path.cwd()
        if name is None:
            self.testname = Path(self.elf).stem
        else:
            self.testname = name
        self.cmd = []
        self.log = None
        self.process = None
        self.expected_retcode = int(retcode)

    def launch(self, dry_run=None):
        """Launch the simulation.

        Launch the simulation by invoking the command stored in the
        `cmd` attribute of the class. Subclasses are required to define
        a non-empty `cmd` attribute prior to invoking this method.

        Arguments:
            dry_run: A preview of the simulation command is displayed
                without actually launching the simulation.
        """
        # Override dry_run setting at launch time
        if dry_run is not None:
            self.dry_run = dry_run

        # Print launch message and simulation command
        cprint(f'Run test {colored(self.elf, "cyan")}', attrs=["bold"])
        cmd_string = ' '.join(self.cmd)
        print(f'[{self.run_dir}]$ {cmd_string}', flush=True)

        # Launch simulation if not doing a dry run
        if not self.dry_run:
            # Create run directory and log file
            os.makedirs(self.run_dir, exist_ok=True)
            self.log = self.run_dir / self.LOG_FILE
            # Launch simulation subprocess
            with open(self.log, 'w') as f:
                self.process = subprocess.Popen(self.cmd, stdout=f, stderr=subprocess.STDOUT,
                                                cwd=self.run_dir, universal_newlines=True)

    def launched(self):
        """Return whether the simulation was launched."""
        if self.process:
            return True
        else:
            return False

    def completed(self):
        """Return whether the simulation completed."""
        if self.dry_run:
            return True
        elif self.process:
            return self.process.poll() is not None
        else:
            return False

    def get_retcode(self):
        """Get the return code of the simulation."""
        if self.dry_run:
            return 0
        else:
            if self.completed():
                return int(self.process.returncode)

    def successful(self):
        """Return whether the simulation was successful."""
        actual_retcode = self.get_retcode()
        if actual_retcode is not None:
            return int(actual_retcode) == int(self.expected_retcode)
        else:
            return False

    def get_simulation_time(self):
        """Return the execution time [ns] of the binary in simulation."""
        return None

    def get_cpu_time(self):
        """Return the CPU time [s] taken to run the simulation."""
        return None

    def print_log(self):
        """Print a log of the simulation to stdout."""
        with open(self.log, 'r') as f:
            print(f.read())

    def print_status(self):
        """Print a status message to stdout.

        The status message reports whether the test is still running
        or, if it completed, whether it was successful or failed.
        """
        if self.completed():
            if self.successful():
                cprint(f'{self.elf} test passed', 'green', attrs=['bold'], flush=True)
            else:
                cprint(f'{self.elf} test failed', 'red', attrs=['bold'], flush=True)
        elif self.launched():
            cprint(f'{self.elf} test running', 'yellow', attrs=['bold'], flush=True)
        else:
            cprint(f'{self.elf} test not launched', 'yellow', attrs=['bold'], flush=True)

__init__(elf=None, dry_run=False, retcode=0, run_dir=None, name=None)

Constructor for the Simulation class.

A Simulation object is defined at a minimum by a software binary to be simulated on the desired hardware. The hardware is implicitly determined by the simulation command.

Parameters:

Name Type Description Default
elf

The software binary to simulate.

None
run_dir

The directory where to launch the simulation command. If none is passed, the current working directory is assumed.

None
dry_run

A preview of the simulation command will be displayed without actually launching the simulation.

False
Source code in util/sim/Simulation.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def __init__(self, elf=None, dry_run=False, retcode=0, run_dir=None, name=None):
    """Constructor for the Simulation class.

    A Simulation object is defined at a minimum by a software
    binary to be simulated on the desired hardware. The hardware is
    implicitly determined by the simulation command.

    Arguments:
        elf: The software binary to simulate.
        run_dir: The directory where to launch the simulation
            command. If none is passed, the current working
            directory is assumed.
        dry_run: A preview of the simulation command will be
            displayed without actually launching the simulation.
    """
    self.elf = elf
    self.dry_run = dry_run
    self.run_dir = run_dir if run_dir is not None else Path.cwd()
    if name is None:
        self.testname = Path(self.elf).stem
    else:
        self.testname = name
    self.cmd = []
    self.log = None
    self.process = None
    self.expected_retcode = int(retcode)

completed()

Return whether the simulation completed.

Source code in util/sim/Simulation.py
84
85
86
87
88
89
90
91
def completed(self):
    """Return whether the simulation completed."""
    if self.dry_run:
        return True
    elif self.process:
        return self.process.poll() is not None
    else:
        return False

get_cpu_time()

Return the CPU time [s] taken to run the simulation.

Source code in util/sim/Simulation.py
113
114
115
def get_cpu_time(self):
    """Return the CPU time [s] taken to run the simulation."""
    return None

get_retcode()

Get the return code of the simulation.

Source code in util/sim/Simulation.py
93
94
95
96
97
98
99
def get_retcode(self):
    """Get the return code of the simulation."""
    if self.dry_run:
        return 0
    else:
        if self.completed():
            return int(self.process.returncode)

get_simulation_time()

Return the execution time [ns] of the binary in simulation.

Source code in util/sim/Simulation.py
109
110
111
def get_simulation_time(self):
    """Return the execution time [ns] of the binary in simulation."""
    return None

launch(dry_run=None)

Launch the simulation.

Launch the simulation by invoking the command stored in the cmd attribute of the class. Subclasses are required to define a non-empty cmd attribute prior to invoking this method.

Parameters:

Name Type Description Default
dry_run

A preview of the simulation command is displayed without actually launching the simulation.

None
Source code in util/sim/Simulation.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def launch(self, dry_run=None):
    """Launch the simulation.

    Launch the simulation by invoking the command stored in the
    `cmd` attribute of the class. Subclasses are required to define
    a non-empty `cmd` attribute prior to invoking this method.

    Arguments:
        dry_run: A preview of the simulation command is displayed
            without actually launching the simulation.
    """
    # Override dry_run setting at launch time
    if dry_run is not None:
        self.dry_run = dry_run

    # Print launch message and simulation command
    cprint(f'Run test {colored(self.elf, "cyan")}', attrs=["bold"])
    cmd_string = ' '.join(self.cmd)
    print(f'[{self.run_dir}]$ {cmd_string}', flush=True)

    # Launch simulation if not doing a dry run
    if not self.dry_run:
        # Create run directory and log file
        os.makedirs(self.run_dir, exist_ok=True)
        self.log = self.run_dir / self.LOG_FILE
        # Launch simulation subprocess
        with open(self.log, 'w') as f:
            self.process = subprocess.Popen(self.cmd, stdout=f, stderr=subprocess.STDOUT,
                                            cwd=self.run_dir, universal_newlines=True)

launched()

Return whether the simulation was launched.

Source code in util/sim/Simulation.py
77
78
79
80
81
82
def launched(self):
    """Return whether the simulation was launched."""
    if self.process:
        return True
    else:
        return False

print_log()

Print a log of the simulation to stdout.

Source code in util/sim/Simulation.py
117
118
119
120
def print_log(self):
    """Print a log of the simulation to stdout."""
    with open(self.log, 'r') as f:
        print(f.read())

print_status()

Print a status message to stdout.

The status message reports whether the test is still running or, if it completed, whether it was successful or failed.

Source code in util/sim/Simulation.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def print_status(self):
    """Print a status message to stdout.

    The status message reports whether the test is still running
    or, if it completed, whether it was successful or failed.
    """
    if self.completed():
        if self.successful():
            cprint(f'{self.elf} test passed', 'green', attrs=['bold'], flush=True)
        else:
            cprint(f'{self.elf} test failed', 'red', attrs=['bold'], flush=True)
    elif self.launched():
        cprint(f'{self.elf} test running', 'yellow', attrs=['bold'], flush=True)
    else:
        cprint(f'{self.elf} test not launched', 'yellow', attrs=['bold'], flush=True)

successful()

Return whether the simulation was successful.

Source code in util/sim/Simulation.py
101
102
103
104
105
106
107
def successful(self):
    """Return whether the simulation was successful."""
    actual_retcode = self.get_retcode()
    if actual_retcode is not None:
        return int(actual_retcode) == int(self.expected_retcode)
    else:
        return False

VCSSimulation

Bases: QuestaVCSSimulation

An RTL simulation running on VCS.

Source code in util/sim/Simulation.py
270
271
272
273
274
275
276
277
278
279
280
281
282
class VCSSimulation(QuestaVCSSimulation):
    """An RTL simulation running on VCS."""

    def get_cpu_time(self):
        # Extract the CPU time from the simulation log
        if self.log is not None:
            with open(self.log, 'r') as f:
                for line in f.readlines():
                    regex = r'CPU Time: \s*([\d.]+) seconds'
                    match = re.search(regex, line)
                    if match:
                        seconds = float(match.group(1))
                        return seconds

VerilatorSimulation

Bases: RTLSimulation

An RTL simulation running on Verilator.

The return code of the simulation is returned directly as the return code of the command launching the simulation.

Source code in util/sim/Simulation.py
178
179
180
181
182
183
184
class VerilatorSimulation(RTLSimulation):
    """An RTL simulation running on Verilator.

    The return code of the simulation is returned directly as the
    return code of the command launching the simulation.
    """
    pass