Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

arc: Add support for cooperative sharing of VPX vector registers #78116

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions arch/arc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ config ARC_NORMAL_FIRMWARE
resources of the ARC processors, and, therefore, it shall avoid
accessing them.

config ARC_VPX_COOPERATIVE_SHARING
bool "Cooperative sharing of ARC VPX vector registers"
select SCHED_CPU_MASK if MP_MAX_NUM_CPUS > 1
help
This option enables the cooperative sharing of the ARC VPX vector
registers. Threads that want to use those registers must successfully
call arc_vpx_lock() before using them, and call arc_vpx_unlock()
when done using them.

source "arch/arc/core/dsp/Kconfig"

menu "ARC MPU Options"
Expand Down
69 changes: 68 additions & 1 deletion arch/arc/core/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@
#include <zephyr/arch/arc/v2/mpu/arc_core_mpu.h>
#endif

#if defined(CONFIG_ARC_DSP) && defined(CONFIG_DSP_SHARING)
#if defined(CONFIG_ARC_VPX_COOPERATIVE_SHARING) || defined(CONFIG_DSP_SHARING)
#include <zephyr/arch/arc/v2/dsp/arc_dsp.h>
static struct k_spinlock lock;
#endif

#if defined(CONFIG_ARC_VPX_COOPERATIVE_SHARING)
static struct k_sem vpx_sem[CONFIG_MP_MAX_NUM_CPUS];
#endif

/* initial stack frame */
struct init_stack_frame {
uintptr_t pc;
Expand Down Expand Up @@ -320,3 +325,65 @@ void arc_dsp_enable(struct k_thread *thread, unsigned int options)
k_spin_unlock(&lock, key);
}
#endif /* CONFIG_ARC_DSP && CONFIG_DSP_SHARING */

#if defined(CONFIG_ARC_VPX_COOPERATIVE_SHARING)
int arc_vpx_lock(k_timeout_t timeout)
{
k_spinlock_key_t key;
unsigned int id;

key = k_spin_lock(&lock);

id = _current_cpu->id;
#if (CONFIG_MP_MAX_NUM_CPUS > 1) && defined(CONFIG_SCHED_CPU_MASK)
__ASSERT(!arch_is_in_isr() && (_current->base.cpu_mask == BIT(id)), "");
#endif
k_spin_unlock(&lock, key);

/*
* It is assumed that the thread is (still) pinned to
* the same CPU identified by <id>.
*/

return k_sem_take(&vpx_sem[id], timeout);
}

void arc_vpx_unlock(void)
{
k_spinlock_key_t key;
unsigned int id;

key = k_spin_lock(&lock);
#if (CONFIG_MP_MAX_NUM_CPUS > 1) && defined(CONFIG_SCHED_CPU_MASK)
__ASSERT(!arch_is_in_isr() && (_current->base.cpu_mask == BIT(id)), "");
#endif
id = _current_cpu->id;
k_spin_unlock(&lock, key);

/*
* It is assumed that this thread is (still) pinned to
* the CPU identified by <id>, and that it is the same CPU
* used by arc_vpx_lock().
*/

k_sem_give(&vpx_sem[id]);
}

void arc_vpx_unlock_force(unsigned int id)
{
__ASSERT(id < CONFIG_MP_MAX_NUM_CPUS, "");

k_sem_give(&vpx_sem[id]);
}

static int arc_vpx_sem_init(void)
{
for (unsigned int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) {
k_sem_init(vpx_sem, 1, 1);
}

return 0;
}

SYS_INIT(arc_vpx_sem_init, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
#endif
20 changes: 20 additions & 0 deletions doc/hardware/arch/arc-support-status.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,23 @@ Notes
.. [#f6] currently only ARC VPX scalar port is supported. The support of VPX vector pipeline, VCCM,
STU is not included in this port, and require additional development and / or other runtime
integration.

VPX Vector Registers
--------------------
Zephyr supports a limited form sharing of the VPX vector registers known as
cooperative sharing. Threads that use these registers must bookend the relevant
sections with calls to :c:func:`arc_vpx_lock` and :c:func:`arc_vpx_unlock` to
control access to this resource.

.. note::
If the system has multiple CPUs, then it is the responsibility of the
application developer to both pin the thread to a single CPU before it
attempts to get the cooperative lock, and not modify the CPU affinity
while it is waiting for or holding that cooperative lock.

Configuration Options
=====================

The cooperative sharing of the VPX vector registers is selected when
configuration option :kconfig:option:`CONFIG_ARC_VPX_COOPERATIVE_SHARING`
is enabled.
54 changes: 54 additions & 0 deletions include/zephyr/arch/arc/v2/vpx/arc_vpx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2024 Synopsys.
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_ARCH_ARC_V2_VPX_ARC_VPX_H_
#define ZEPHYR_INCLUDE_ARCH_ARC_V2_VPX_ARC_VPX_H_

#include <zephyr/sys_clock.h>

/**
* @brief Obtain a cooperative lock on the VPX vector registers
*
* This function is used to obtain a cooperative lock on the current CPU's
* VPX vector registers before the calling thread uses them. Callers
* attempting to obtain the cooperative lock must be already restricted to
* executing on a single CPU, and continue to execute on that same CPU while
* both waiting and holding the lock.
*
* This routine is not callable from an ISR.
*
* @param timeout Waiting period to obtain the lock, or one of the special
* values K_NO_WAIT and K_FOREVER.
*
* @return Zero on success, otherwise error code
*/
int arc_vpx_lock(k_timeout_t timeout);

/**
* @brief Release cooperative lock on the VPX vector registers
*
* This function is used to release the cooperative lock on the current CPU's
* VPX vector registers. It is called after the current thread no longer needs
* to use the VPX vector registers, thereby allowing another thread to use them.
*
* This routine is not callable from an ISR.
*/
void arc_vpx_unlock(void);

/**
* @brief Release cooperative lock on a CPU's VPX vector registers
*
* This function is used to release the cooperative lock on the specified CPU's
* VPX vector registers. This routine should not be used except by a system
* monitor to release the cooperative lock in case the locking thread where it
* is known that the locking thread is unable to release it (e.g. it was
* aborted while holding the lock).
*
* @param cpu_id CPU ID of the VPX vector register set to be unlocked
*/
void arc_vpx_unlock_force(unsigned int cpu_id);

#endif /* ZEPHYR_INCLUDE_ARCH_ARC_V2_VPX_ARC_VPX_H_ */
15 changes: 15 additions & 0 deletions tests/arch/arc/arc_vpx_lock/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2024 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(arc_vpx_lock)

target_sources(app PRIVATE
src/main.c
)

if(COMPILER STREQUAL arcmwdt)
zephyr_include_directories(${ARCMWDT_TOOLCHAIN_PATH}/MetaWare/arc/lib/src/fx/include/)
endif()
12 changes: 12 additions & 0 deletions tests/arch/arc/arc_vpx_lock/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Title: ARC VPX Lock

Description:

This test verifies that the ARC VPX lock/unlock mechanism used to bookend
code that uses the ARC VPX vector registers works correctly. As this VPX
lock/unlock mechanism does not technically require those registers to be
used to control access to them (they bookend the relevant code sections),
the test does not actually access those VPX registers.

However, it does check that the system behaves as expected when the ARC VPX
lock/unlock mechanism is used.
3 changes: 3 additions & 0 deletions tests/arch/arc/arc_vpx_lock/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_MAIN_STACK_SIZE=1024
CONFIG_ARC_VPX_COOPERATIVE_SHARING=y
148 changes: 148 additions & 0 deletions tests/arch/arc/arc_vpx_lock/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2024 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/ztest.h>

#include <zephyr/arch/arc/v2/vpx/arc_vpx.h>

#ifndef CONFIG_ARC_VPX_COOPERATIVE_SHARING
#error "Rebuild with the ARC_VPX_COOPERATIVE_SHARING config option enabled"
#endif

#define STACK_SIZE (1024 + CONFIG_TEST_EXTRA_STACK_SIZE)

static void timer_func(struct k_timer *timer);

K_THREAD_STACK_DEFINE(payload_stack, STACK_SIZE);

static K_TIMER_DEFINE(my_timer, timer_func, NULL);

static struct k_thread payload_thread;

static volatile int isr_result;
static volatile unsigned int isr_vpx_lock_id;

/**
* Obtain the current CPU id.
*/
static int current_cpu_id_get(void)
{
int key;
int id;

key = arch_irq_lock();
id = _current_cpu->id;
arch_irq_unlock(key);

return id;
}

static void timer_func(struct k_timer *timer)
{
arc_vpx_unlock_force(isr_vpx_lock_id);
}

static void arc_vpx_lock_unlock_timed_payload(void *p1, void *p2, void *p3)
{
int status;
unsigned int cpu_id;
unsigned int lock_id;

cpu_id = (unsigned int)(uintptr_t)(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);

status = arc_vpx_lock(K_NO_WAIT);
zassert_equal(0, status, "Expected return value %d, not %d\n", 0, status);

/*
* In 1 second, forcibly release the VPX lock. However, wait up to
* 5 seconds before considering this a failure.
*/

isr_vpx_lock_id = cpu_id;
k_timer_start(&my_timer, K_MSEC(1000), K_FOREVER);

status = arc_vpx_lock(K_MSEC(5000));
zassert_equal(0, status, "Expected return value %d, not %d\n", 0, status);

arc_vpx_unlock();
}

ZTEST(vpx_lock, test_arc_vpx_lock_unlock_timed)
{
int priority;
int cpu_id;

priority = k_thread_priority_get(k_current_get());
cpu_id = current_cpu_id_get();

k_thread_create(&payload_thread, payload_stack, STACK_SIZE,
arc_vpx_lock_unlock_timed_payload,
(void *)(uintptr_t)cpu_id, NULL, NULL,
priority - 2, 0, K_FOREVER);

Check notice on line 86 in tests/arch/arc/arc_vpx_lock/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/arch/arc/arc_vpx_lock/src/main.c:86 - arc_vpx_lock_unlock_timed_payload, - (void *)(uintptr_t)cpu_id, NULL, NULL, + arc_vpx_lock_unlock_timed_payload, (void *)(uintptr_t)cpu_id, NULL, NULL,

#if defined(CONFIG_SCHED_CPU_MASK) && (CONFIG_MP_MAX_NUM_CPUS > 1)
k_thread_cpu_pin(&payload_thread, cpu_id);
#endif
k_thread_start(&payload_thread);

k_thread_join(&payload_thread, K_FOREVER);
}

static void arc_vpx_lock_unlock_payload(void *p1, void *p2, void *p3)
{
int status;

ARG_UNUSED(p1);
ARG_UNUSED(p2);
ARG_UNUSED(p3);

/* The VPX lock is available; take it. */

status = arc_vpx_lock(K_NO_WAIT);
zassert_equal(0, status, "Expected return value %d, not %d\n", 0, status);

/* The VPX lock has already been taken; expect errors */

status = arc_vpx_lock(K_NO_WAIT);
zassert_equal(-EBUSY, status, "Expected return value %d (-EBUSY), not %d\n",
-EBUSY, status);

status = arc_vpx_lock(K_MSEC(10));
zassert_equal(-EAGAIN, status, "Expected return value %d (-EAGAIN), not %d\n",
-EAGAIN, status);

Check notice on line 118 in tests/arch/arc/arc_vpx_lock/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/arch/arc/arc_vpx_lock/src/main.c:118 - zassert_equal(-EBUSY, status, "Expected return value %d (-EBUSY), not %d\n", - -EBUSY, status); + zassert_equal(-EBUSY, status, "Expected return value %d (-EBUSY), not %d\n", -EBUSY, + status); status = arc_vpx_lock(K_MSEC(10)); - zassert_equal(-EAGAIN, status, "Expected return value %d (-EAGAIN), not %d\n", - -EAGAIN, status); + zassert_equal(-EAGAIN, status, "Expected return value %d (-EAGAIN), not %d\n", -EAGAIN, + status);
/* Verify that unlocking makes it available */

arc_vpx_unlock();

status = arc_vpx_lock(K_NO_WAIT);
zassert_equal(0, status, "Expected return value %d, not %d\n", 0, status);
arc_vpx_unlock();
}

ZTEST(vpx_lock, test_arc_vpx_lock_unlock)
{
int priority;
int cpu_id;

priority = k_thread_priority_get(k_current_get());
cpu_id = current_cpu_id_get();

k_thread_create(&payload_thread, payload_stack, STACK_SIZE,
arc_vpx_lock_unlock_payload, NULL, NULL, NULL,
priority - 2, 0, K_FOREVER);

Check notice on line 139 in tests/arch/arc/arc_vpx_lock/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/arch/arc/arc_vpx_lock/src/main.c:139 - k_thread_create(&payload_thread, payload_stack, STACK_SIZE, - arc_vpx_lock_unlock_payload, NULL, NULL, NULL, - priority - 2, 0, K_FOREVER); + k_thread_create(&payload_thread, payload_stack, STACK_SIZE, arc_vpx_lock_unlock_payload, + NULL, NULL, NULL, priority - 2, 0, K_FOREVER);
#if defined(CONFIG_SCHED_CPU_MASK) && (CONFIG_MP_MAX_NUM_CPUS > 1)
k_thread_cpu_pin(&payload_thread, cpu_id);
#endif
k_thread_start(&payload_thread);

k_thread_join(&payload_thread, K_FOREVER);
}

ZTEST_SUITE(vpx_lock, NULL, NULL, NULL, NULL, NULL);
11 changes: 11 additions & 0 deletions tests/arch/arc/arc_vpx_lock/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
tests:
arch.arc.vpx_lock:
filter: CONFIG_ISA_ARCV2
toolchain_allow: arcmwdt
platform_allow: nsim/nsim_vpx5
arch.arc.vpx_lock.cpu_mask:
filter: CONFIG_ISA_ARCV2 and (CONFIG_MP_MAX_NUM_CPUS > 1)
toolchain_allow: arcmwdt
platform_allow: nsim/nsim_vpx5
extra_configs:
- CONFIG_SCHED_CPU_MASK=y
Loading