Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
optimsoc/test/systemtest/test_tutorial.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
464 lines (389 sloc)
18.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (c) 2016-2018 by the author(s) | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
# | |
# Author(s): | |
# Philipp Wagner <philipp.wagner@tum.de> | |
import os | |
import subprocess | |
import pytest | |
import logging | |
import time | |
import re | |
import util | |
logging.basicConfig(level=logging.DEBUG) | |
class TestTutorial: | |
""" | |
Test all simulated examples in the User Guide Tutorial | |
The goal of this test case is to ensure that the tutorials as described in | |
the user guide continue to work after changes to OpTiMSoC. It therefore | |
tests only the examples as given in the tutorial, and does not aim for | |
full coverage of the used parts of OpTiMSoC. | |
""" | |
@pytest.fixture | |
def sim_system_2x2_cccc_sim_dualcore_debug(self, tmpdir): | |
cmd_sim = '{}/examples/sim/system_2x2_cccc/system_2x2_cccc_sim_dualcore_debug'.format(os.environ['OPTIMSOC']) | |
p_sim = util.Process(cmd_sim, logdir=str(tmpdir), cwd=str(tmpdir), | |
startup_done_expect='Glip TCP DPI listening', | |
startup_timeout=10) | |
p_sim.run() | |
yield p_sim | |
p_sim.terminate() | |
def test_baremetal_hello(self, baremetal_apps_hello): | |
""" | |
Ensure that all files which are mentioned to result from compiling the | |
hello world example are actually being generated. | |
""" | |
assert baremetal_apps_hello.join('hello.elf').isfile() | |
assert baremetal_apps_hello.join('hello.dis').isfile() | |
assert baremetal_apps_hello.join('hello.bin').isfile() | |
assert baremetal_apps_hello.join('hello.vmem').isfile() | |
def test_tutorial1(self, baremetal_apps_hello, tmpdir): | |
""" | |
Tutorial 1: Run hello world on compute_tile_dm with a single core | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/compute_tile/compute_tile_sim_singlecore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello.join('hello.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check if output files exist | |
assert tmpdir.join('stdout.000').isfile() | |
# compare output to golden reference | |
assert util.matches_golden_reference(str(tmpdir), 'stdout.000', | |
filter_func=util.filter_timestamps) | |
def test_tutorial2(self, baremetal_apps_hello, tmpdir): | |
""" | |
Tutorial 2: generate a VCD file | |
""" | |
cmd = ['{}/examples/sim/compute_tile/compute_tile_sim_singlecore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello.join('hello.vmem'))), | |
'--vcd'] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# XXX: We currently only check if the VCD file is written, not if it | |
# contains meaningful content. | |
assert tmpdir.join('sim.vcd').isfile() | |
def test_tutorial3_dualcore(self, baremetal_apps_hello, tmpdir): | |
""" | |
Tutorial 3: like tutorial 1, just using two cores | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/compute_tile/compute_tile_sim_dualcore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello.join('hello.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check all output files | |
for f in ['stdout.000', 'stdout.001']: | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
def test_tutorial3_quadcore(self, baremetal_apps_hello, tmpdir): | |
""" | |
Tutorial 3: like tutorial 1, just using four cores | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/compute_tile/compute_tile_sim_quadcore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello.join('hello.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check all output files | |
for f in ['stdout.000', 'stdout.001', 'stdout.002', 'stdout.003']: | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
def test_tutorial4_hello(self, baremetal_apps_hello, tmpdir): | |
""" | |
Tutorial 4: Run hello world application on 2x2 CCCC in Verilator | |
Memory loading is done through Verilator meminit. | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/system_2x2_cccc/system_2x2_cccc_sim_dualcore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello.join('hello.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check all output files | |
for i in range(0, 7): | |
f = "stdout.{:03d}".format(i) | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
def test_tutorial4_hello_mpsimple(self, baremetal_apps_hello_mpsimple, tmpdir): | |
""" | |
Tutorial 4: Run hello_mpsimple world application on 2x2 CCCC in Verilator | |
Memory loading is done through Verilator meminit. | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/system_2x2_cccc/system_2x2_cccc_sim_dualcore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello_mpsimple.join('hello_mpsimple.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check all output files | |
for i in range(0, 7): | |
f = "stdout.{:03d}".format(i) | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
def test_tutorial5_hello_notrace(self, tmpdir, baremetal_apps_hello, | |
sim_system_2x2_cccc_sim_dualcore_debug): | |
""" | |
Tutorial 5: use osd-target-run and system with debug system to run | |
software (no trace enabled) | |
""" | |
hello_elf = str(baremetal_apps_hello.join('hello.elf')) | |
cmd_targetrun = ['osd-target-run', | |
'-e', hello_elf, | |
'-vvv'] | |
p_targetrun = util.Process(cmd_targetrun, logdir=str(tmpdir), | |
cwd=str(tmpdir)) | |
p_targetrun.run() | |
logging.getLogger(__name__).info("Program should terminate itself; give it up to 5 minutes") | |
p_targetrun.proc.wait(timeout=300) | |
assert p_targetrun.proc.returncode == 0 | |
logging.getLogger(__name__).info("Now wait 60 seconds until the program has finished execution") | |
time.sleep(60) | |
try: | |
p_targetrun.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass | |
# check the simulation-generated stdout files | |
for i in range(0, 7): | |
f = "stdout.{:03d}".format(i) | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
def test_tutorial5_hello_systrace(self, tmpdir, baremetal_apps_hello, | |
sim_system_2x2_cccc_sim_dualcore_debug): | |
""" | |
Tutorial 5: use osd-target-run and system with debug system to run | |
software (with system trace) | |
""" | |
startup_done_string = '[INFO] osd-target-run: System is now running. Press CTRL-C to end tracing.' | |
hello_elf = str(baremetal_apps_hello.join('hello.elf')) | |
cmd_targetrun = ['osd-target-run', | |
'-e', hello_elf, | |
'--systrace', | |
'-vvv'] | |
p_targetrun = util.Process(cmd_targetrun, logdir=str(tmpdir), | |
cwd=str(tmpdir), | |
startup_done_expect=startup_done_string, | |
startup_timeout=300) | |
p_targetrun.run() | |
logging.getLogger(__name__).info("Record traces for 1 minute") | |
time.sleep(60) | |
p_targetrun.send_ctrl_c() | |
# Give the process some time to clean up | |
p_targetrun.proc.wait(timeout=30) | |
assert p_targetrun.proc.returncode == 0 | |
try: | |
p_targetrun.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass | |
# Ensure that the STM logs are written | |
stmlogs = [ 'systrace.0002.log', 'systrace.0004.log', | |
'systrace.0007.log', 'systrace.0009.log', | |
'systrace.0012.log', 'systrace.0014.log', | |
'systrace.0017.log', 'systrace.0019.log'] | |
for f in stmlogs: | |
# STM log file exists | |
assert tmpdir.join(f).isfile() | |
# STM log >= 0 bytes | |
# Currently some of the STM logs don't contain a full trace due to | |
# overload in the debug system causing dropped packets. Checking for | |
# such partial logs cannot be done reliably. | |
f_stat = os.stat(str(tmpdir.join(f))) | |
assert f_stat.st_size > 0 | |
def test_tutorial5_hello_coretrace(self, tmpdir, baremetal_apps_hello, | |
sim_system_2x2_cccc_sim_dualcore_debug): | |
""" | |
Tutorial 5: use osd-target-run and system with debug system to run | |
software (with core trace) | |
""" | |
startup_done_string = '[INFO] osd-target-run: System is now running. Press CTRL-C to end tracing.' | |
hello_elf = str(baremetal_apps_hello.join('hello.elf')) | |
cmd_targetrun = ['osd-target-run', | |
'-e', hello_elf, | |
'--coretrace', | |
'-vvv'] | |
p_targetrun = util.Process(cmd_targetrun, logdir=str(tmpdir), | |
cwd=str(tmpdir), | |
startup_done_expect=startup_done_string, | |
startup_timeout=300) | |
p_targetrun.run() | |
logging.getLogger(__name__).info("Record traces for 1 minute") | |
time.sleep(60) | |
p_targetrun.send_ctrl_c() | |
# Give the process some time to clean up | |
p_targetrun.proc.wait(timeout=30) | |
assert p_targetrun.proc.returncode == 0 | |
try: | |
p_targetrun.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass | |
# Ensure that the CTM logs are written | |
ctmlogs = [ 'coretrace.0003.log', 'coretrace.0005.log', | |
'coretrace.0008.log', 'coretrace.0010.log', | |
'coretrace.0013.log', 'coretrace.0015.log', | |
'coretrace.0018.log', 'coretrace.0020.log'] | |
for f in ctmlogs: | |
# CTM log file exists | |
assert tmpdir.join(f).isfile() | |
# CTM log >= 0 bytes | |
# Currently some of the CTM logs don't contain a full trace due to | |
# overload in the debug system causing dropped packets. Checking for | |
# such partial logs cannot be done reliably. | |
f_stat = os.stat(str(tmpdir.join(f))) | |
assert f_stat.st_size > 0 | |
def test_tutorial6_hello_mp(self, baremetal_apps_hello_mp, tmpdir): | |
""" | |
Tutorial 8: Run hello_mp world application on 2x2 CCCC in Verilator | |
Memory loading is done through Verilator meminit. | |
""" | |
# run simulation | |
cmd = ['{}/examples/sim/system_2x2_cccc/system_2x2_cccc_sim_dualcore'.format(os.environ['OPTIMSOC']), | |
'--meminit={}'.format(str(baremetal_apps_hello_mp.join('hello_mp.vmem')))] | |
subprocess.check_output(cmd, cwd=str(tmpdir)) | |
# check all output files | |
for i in range(0, 7): | |
f = "stdout.{:03d}".format(i) | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_timestamps) | |
class TestTutorialFpga: | |
""" | |
Test all FPGA-based tutorial steps in the User Guide | |
The tests in this class require the same hardware as used in the User Guide | |
to be connected to the node that executes this test. Currently, this is the | |
Nexus 4 DDR board. | |
""" | |
def test_tutorial7(self, tmpdir, localconf, baremetal_apps_hello): | |
""" | |
Tutorial 7: Program a compute_tile system to a Nexys4 DDR board and run | |
hello world on it. | |
""" | |
# program FPGA with bitstream | |
bitstream = "{}/examples/fpga/nexys4ddr/compute_tile/compute_tile_nexys4ddr_singlecore.bit".format(os.environ['OPTIMSOC']) | |
cmd_pgm = ['optimsoc-pgm-fpga', bitstream, 'xc7a100t_0'] | |
p_pgm = util.Process(cmd_pgm, logdir=str(tmpdir), cwd=str(tmpdir)) | |
p_pgm.run() | |
p_pgm.proc.wait(timeout=300) | |
assert p_pgm.proc.returncode == 0 | |
time.sleep(2) | |
# run hello.elf on target board | |
nexys4ddr_device = localconf['boards']['nexys4ddr']['device'] | |
logging.getLogger(__name__).info("Using Nexys 4 DDR board connected to " + nexys4ddr_device) | |
glip_backend = 'uart' | |
glip_backend_options = 'device=%s,speed=12000000' % nexys4ddr_device | |
hello_elf = str(baremetal_apps_hello.join('hello.elf')) | |
cmd_targetrun = ['osd-target-run', | |
'-e', hello_elf, | |
'-b', glip_backend, | |
'-o', glip_backend_options, | |
'--coretrace', | |
'--systrace', | |
'--verify', | |
'-vvv'] | |
startup_done_string = '[INFO] osd-target-run: System is now running. Press CTRL-C to end tracing.' | |
p_targetrun = util.Process(cmd_targetrun, logdir=str(tmpdir), | |
cwd=str(tmpdir), | |
startup_done_expect=startup_done_string, | |
startup_timeout=300) | |
p_targetrun.run() | |
logging.getLogger(__name__).info("Record traces for 30 seconds") | |
time.sleep(30) | |
p_targetrun.send_ctrl_c() | |
# Give the process some time to clean up | |
p_targetrun.proc.wait(timeout=30) | |
assert p_targetrun.proc.returncode == 0 | |
try: | |
p_targetrun.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass | |
# Ensure that the STM logs are written | |
stmlogs = [ 'systrace.0002.log' ] | |
for f in stmlogs: | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_osd_trace_timestamps) | |
# Ensure that the CTM logs are written | |
ctmlogs = [ 'coretrace.0003.log' ] | |
for f in ctmlogs: | |
assert tmpdir.join(f).isfile() | |
assert util.matches_golden_reference(str(tmpdir), f, | |
filter_func=util.filter_osd_trace_timestamps) | |
def test_tutorial_linux(self, tmpdir, localconf, linux_compute_tile_singlecore): | |
""" | |
Tutorial 8: Execute the Linux example in the tutorial | |
""" | |
# program FPGA with bitstream | |
bitstream = "{}/examples/fpga/nexys4ddr/compute_tile/compute_tile_nexys4ddr_singlecore.bit".format(os.environ['OPTIMSOC']) | |
cmd_pgm = ['optimsoc-pgm-fpga', bitstream, 'xc7a100t_0'] | |
p_pgm = util.Process(cmd_pgm, logdir=str(tmpdir), cwd=str(tmpdir)) | |
p_pgm.run() | |
p_pgm.proc.wait(timeout=300) | |
assert p_pgm.proc.returncode == 0 | |
time.sleep(2) | |
# run hello.elf on target board | |
nexys4ddr_device = localconf['boards']['nexys4ddr']['device'] | |
logging.getLogger(__name__).info("Using Nexys 4 DDR board connected to " + nexys4ddr_device) | |
glip_backend = 'uart' | |
glip_backend_options = 'device=%s,speed=12000000' % nexys4ddr_device | |
vmlinux = str(linux_compute_tile_singlecore) | |
cmd_targetrun = ['osd-target-run', | |
'-e', vmlinux, | |
'-b', glip_backend, | |
'-o', glip_backend_options, | |
'--systrace', | |
'--verify', | |
'--terminal', | |
'-vvv'] | |
startup_done_string = '[INFO] osd-target-run: System is now running. Press CTRL-C to end tracing.' | |
p_targetrun = util.Process(cmd_targetrun, logdir=str(tmpdir), | |
cwd=str(tmpdir), | |
startup_done_expect=startup_done_string, | |
startup_timeout=300) | |
p_targetrun.run() | |
# Find allocated pseudo-terminal (expected to appear within 10 seconds) | |
tty_dev = None | |
search = re.compile(r'^.+DEM-UART pseudo-terminal available at (/dev/pts/\d+)') | |
match = p_targetrun._find_in_output(search, timeout = 5) | |
tty_dev = match.group(1) | |
assert tty_dev | |
logging.getLogger(__name__).info("Linux console available at " + tty_dev) | |
# Wait for Linux to boot | |
logging.getLogger(__name__).info("Run Linux until we get a login prompt") | |
cmd_tty_cat = ['cat', tty_dev] | |
p_tty_cat = util.Process(cmd_tty_cat, logdir=str(tmpdir), | |
cwd=str(tmpdir), | |
startup_done_expect="Welcome to OpTiMSoC", | |
startup_timeout=60) | |
p_tty_cat.run() | |
p_tty_cat.send_ctrl_c() | |
p_tty_cat.proc.wait(timeout=5) | |
try: | |
p_tty_cat.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass | |
p_targetrun.send_ctrl_c() | |
# Give the process some time to clean up | |
p_targetrun.proc.wait(timeout=30) | |
try: | |
p_targetrun.terminate() | |
except ProcessLookupError: | |
# process is already dead | |
pass |