# SPDX-License-Identifier: GPL-2.0 # (C) Copyright 2023, Advanced Micro Devices, Inc. import pytest import random import string import test_net """ Note: This test relies on boardenv_* containing configuration values to define RPU applications information for AMD's ZynqMP SoC which contains, application names, processors, address where it is built, expected output and the tftp load addresses. This test will be automatically skipped without this. It also relies on dhcp or setup_static net test to support tftp to load application on DDR. All the environment parameters are stored sequentially. The length of all parameters values should be same. For example, if 2 app_names are defined in a list as a value of parameter 'app_name' then the other parameters value also should have a list with 2 items. It will run RPU cases for all the applications defined in boardenv_* configuration file. Example: env__zynqmp_rpu_apps = { 'app_name': ['hello_world_r5_0_ddr.elf', 'hello_world_r5_1_ddr.elf'], 'proc': ['rpu0', 'rpu1'], 'cpu_num': [4, 5], 'addr': [0xA00000, 0xB00000], 'output': ['Successfully ran Hello World application on DDR from RPU0', 'Successfully ran Hello World application on DDR from RPU1'], 'tftp_addr': [0x100000, 0x200000], } """ # Get rpu apps params from env def get_rpu_apps_env(u_boot_console): rpu_apps = u_boot_console.config.env.get('env__zynqmp_rpu_apps', False) if not rpu_apps: pytest.skip('ZynqMP RPU application info not defined!') apps = rpu_apps.get('app_name', None) if not apps: pytest.skip('No RPU application found!') procs = rpu_apps.get('proc', None) if not procs: pytest.skip('No RPU application processor provided!') cpu_nums = rpu_apps.get('cpu_num', None) if not cpu_nums: pytest.skip('No CPU number for respective processor provided!') addrs = rpu_apps.get('addr', None) if not addrs: pytest.skip('No RPU application build address found!') outputs = rpu_apps.get('output', None) if not outputs: pytest.skip('Expected output not found!') tftp_addrs = rpu_apps.get('tftp_addr', None) if not tftp_addrs: pytest.skip('TFTP address to load application not found!') return apps, procs, cpu_nums, addrs, outputs, tftp_addrs # Check return code def ret_code(u_boot_console): return u_boot_console.run_command('echo $?') # Initialize tcm def tcminit(u_boot_console, rpu_mode): output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) assert 'Initializing TCM overwrites TCM content' in output return ret_code(u_boot_console) # Load application in DDR def load_app_ddr(u_boot_console, tftp_addr, app): output = u_boot_console.run_command('tftpboot %x %s' % (tftp_addr, app)) assert 'TIMEOUT' not in output assert 'Bytes transferred = ' in output # Load elf u_boot_console.run_command('bootelf -p %x' % tftp_addr) assert ret_code(u_boot_console).endswith('0') # Disable cpus def disable_cpus(u_boot_console, cpu_nums): for num in cpu_nums: u_boot_console.run_command(f'cpu {num} disable') # Load apps on RPU cores def rpu_apps_load(u_boot_console, rpu_mode): apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env( u_boot_console) test_net.test_net_dhcp(u_boot_console) if not test_net.net_set_up: test_net.test_net_setup_static(u_boot_console) try: assert tcminit(u_boot_console, rpu_mode).endswith('0') for i in range(len(apps)): if rpu_mode == 'lockstep' and procs[i] != 'rpu0': continue load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) rel_addr = int(addrs[i] + 0x3C) # Release cpu at app load address cpu_num = cpu_nums[i] cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) output = u_boot_console.run_command(cmd) exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' assert exp_op in output assert f'R5 {rpu_mode} mode' in output u_boot_console.wait_for(outputs[i]) assert ret_code(u_boot_console).endswith('0') finally: disable_cpus(u_boot_console, cpu_nums) @pytest.mark.buildconfigspec('cmd_zynqmp') def test_zynqmp_rpu_app_load_split(u_boot_console): rpu_apps_load(u_boot_console, 'split') @pytest.mark.buildconfigspec('cmd_zynqmp') def test_zynqmp_rpu_app_load_lockstep(u_boot_console): rpu_apps_load(u_boot_console, 'lockstep') @pytest.mark.buildconfigspec('cmd_zynqmp') def test_zynqmp_rpu_app_load_negative(u_boot_console): apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env( u_boot_console) # Invalid commands u_boot_console.run_command('zynqmp tcminit mode') assert ret_code(u_boot_console).endswith('1') rand_str = ''.join(random.choices(string.ascii_lowercase, k=4)) u_boot_console.run_command('zynqmp tcminit %s' % rand_str) assert ret_code(u_boot_console).endswith('1') rand_num = random.randint(2, 100) u_boot_console.run_command('zynqmp tcminit %d' % rand_num) assert ret_code(u_boot_console).endswith('1') test_net.test_net_dhcp(u_boot_console) if not test_net.net_set_up: test_net.test_net_setup_static(u_boot_console) try: rpu_mode = 'split' assert tcminit(u_boot_console, rpu_mode).endswith('0') for i in range(len(apps)): load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) # Run in split mode at different load address rel_addr = int(addrs[i]) + random.randint(200, 1000) cpu_num = cpu_nums[i] cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) output = u_boot_console.run_command(cmd) exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' assert exp_op in output assert f'R5 {rpu_mode} mode' in output assert not outputs[i] in output # Invalid rpu mode rand_str = ''.join(random.choices(string.ascii_lowercase, k=4)) cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rand_str) output = u_boot_console.run_command(cmd) assert exp_op in output assert f'Unsupported mode' in output assert not ret_code(u_boot_console).endswith('0') # Switch to lockstep mode, without disabling CPUs rpu_mode = 'lockstep' u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) assert not ret_code(u_boot_console).endswith('0') # Disable cpus disable_cpus(u_boot_console, cpu_nums) # Switch to lockstep mode, after disabling CPUs output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) assert 'Initializing TCM overwrites TCM content' in output assert ret_code(u_boot_console).endswith('0') # Run lockstep mode for RPU1 for i in range(len(apps)): if procs[i] == 'rpu0': continue load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) rel_addr = int(addrs[i] + 0x3C) cpu_num = cpu_nums[i] cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) output = u_boot_console.run_command(cmd) exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' assert exp_op in output assert f'R5 {rpu_mode} mode' in output assert u_boot_console.p.expect([outputs[i]]) finally: disable_cpus(u_boot_console, cpu_nums) # This forces the console object to be shutdown, so any subsequent test # will reset the board back into U-Boot. u_boot_console.drain_console() u_boot_console.cleanup_spawn()