Skip to content

Simulation

GvsocSimulation

Bases: Simulation

A functional simulation running on GVSOC.

Source code in util/sim/Simulation.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
class GvsocSimulation(Simulation):
    """A functional simulation running on GVSOC."""

    def __init__(self, sim_bin=None, cmd=None, **kwargs):
        super().__init__(**kwargs)

        if cmd is None:
            self.cmd = ['gvsoc', '--target', os.environ.get('GVSOC_TARGET'), '--binary',
                        str(self.elf), 'run']
        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.cmd.append('--simulator=gvsoc')

    def successful(self):
        """Return whether the simulation was successful."""
        # On GVSOC, OpenOCD semi-hosting is used which can just report 0 or 1
        actual_retcode = self.get_retcode()
        if actual_retcode is not None:
            if self.expected_retcode != 0:
                return int(actual_retcode) != 0
            else:
                return int(actual_retcode) == 0
        else:
            return False

successful()

Return whether the simulation was successful.

Source code in util/sim/Simulation.py
292
293
294
295
296
297
298
299
300
301
302
def successful(self):
    """Return whether the simulation was successful."""
    # On GVSOC, OpenOCD semi-hosting is used which can just report 0 or 1
    actual_retcode = self.get_retcode()
    if actual_retcode is not None:
        if self.expected_retcode != 0:
            return int(actual_retcode) != 0
        else:
            return int(actual_retcode) == 0
    else:
        return False

QuestaSimulation

Bases: QuestaVCSSimulation

An RTL simulation running on QuestaSim.

Source code in util/sim/Simulation.py
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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
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
248
249
250
251
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
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
176
177
178
179
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
137
138
139
140
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:
            if self.elf is not None:
                self.testname = Path(self.elf).stem
        else:
            self.testname = name
        self.cmd = []
        self.log = None
        self.process = None
        self.interrupted = False
        self.expected_retcode = int(retcode)
        self.env = None

    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,
                                                env=self.env)

    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 and not self.interrupted
        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
46
47
48
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:
        if self.elf is not None:
            self.testname = Path(self.elf).stem
    else:
        self.testname = name
    self.cmd = []
    self.log = None
    self.process = None
    self.interrupted = False
    self.expected_retcode = int(retcode)
    self.env = None

completed()

Return whether the simulation completed.

Source code in util/sim/Simulation.py
88
89
90
91
92
93
94
95
def completed(self):
    """Return whether the simulation completed."""
    if self.dry_run:
        return True
    elif self.process:
        return self.process.poll() is not None and not self.interrupted
    else:
        return False

get_cpu_time()

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

Source code in util/sim/Simulation.py
117
118
119
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
 97
 98
 99
100
101
102
103
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
113
114
115
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
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
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,
                                            env=self.env)

launched()

Return whether the simulation was launched.

Source code in util/sim/Simulation.py
81
82
83
84
85
86
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
121
122
123
124
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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
105
106
107
108
109
110
111
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
305
306
307
308
309
310
311
312
313
314
315
316
317
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
182
183
184
185
186
187
188
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