[RFC] Adding RISC-V Vector support to RTEMS

Sebastian Huber sebastian.huber at embedded-brains.de
Fri Mar 15 14:29:47 UTC 2024


Hello Ken,

On 14.03.24 18:33, Ken.Unger at microchip.com wrote:
> Hello RTEMS experts,
> 
> We’re in the process of implementing support for RTEMS on a new RISC-V 
> platform.  Among other things, our processor core supports the RISC-V 
> Vector ISA (RVV), with its 32 vector registers which in our case are 512 
> bits (VLEN) deep.   RVV is used by applications to accelerate a variety 
> of code/algorithms utilizing either the auto-vectorizer in GCC/Clang, or 
> through C intrinsics or direct assembly coding.
> 
> My query here is regarding context saving, and options for 
> optimization.  Before I get to that, here’s a few points of background.
> 
> #1. Use of RVV by the compiler (GCC/Clang) is dictated by the ISA string 
> provided, e.g -march=rv64imadcv,  where v is for vector.  The compiler 
> preprocessor symbol "__riscv_vector" can then be used for conditionally 
> compiled code, similar to what is done for floating point.
> 
> #2. Enablement of RVV can be controlled via a machine status CSR.   This 
> allows one to disable RVV entirely (triggering an exception if RVV 
> instructions are executed), but also allows one to track the dirty/clean 
> state of the vector register file.  
> (https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#vector-context-status-in-mstatus <https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#vector-context-status-in-mstatus>).  So, one can conditionally save/restore the registers, although the 2KB of stack space (32 x (512b / 8)) would need to be allocated.
> 
> #3. The C ABI specifies that vector registers are Caller saved.  So, any 
> system call (or any function call for that matter) does not need to 
> preserve these registers.
> 
> A relatively straightforward approach is to say that RTEMS + Application 
> is built with vectors enabled (i.e  -march=rv64imadcv).  One does not 
> need to save/restore the vector registers within Context_Control because 
> of #3, however,

Yes, in this case you don't have to save/restore the vector registers in 
_CPU_Context_switch(). This is similar to the SPARC architecture. For 
SPARC, we implemented a lazy floating point switch 
(cpukit/score/cpu/sparc/include/rtems/score/cpu.h):

/*
  * The SPARC ABI is a bit special with respect to the floating point 
context.
  * The complete floating point context is volatile.  Thus, from an ABI 
point
  * of view nothing needs to be saved and restored during a context switch.
  * Instead the floating point context must be saved and restored during
  * interrupt processing.  Historically, the deferred floating point 
switch was
  * used for SPARC and the complete floating point context is saved and
  * restored during a context switch to the new floating point unit owner.
  * This is a bit dangerous since post-switch actions (e.g. signal handlers)
  * and context switch extensions may silently corrupt the floating point
  * context.
  *
  * The floating point unit is disabled for interrupt handlers.  Thus, 
in case
  * an interrupt handler uses the floating point unit then this will 
result in a
  * trap (INTERNAL_ERROR_ILLEGAL_USE_OF_FLOATING_POINT_UNIT).
  *
  * In uniprocessor configurations, a lazy floating point context switch is
  * used.  In case an active floating point thread is interrupted 
(PSR[EF] == 1)
  * and a thread dispatch is carried out, then this thread is registered 
as the
  * floating point owner.  When a floating point owner is present during a
  * context switch, the floating point unit is disabled for the heir thread
  * (PSR[EF] == 0).  The floating point disabled trap checks that the 
use of the
  * floating point unit is allowed and saves/restores the floating point 
context
  * on demand.
  *
  * In SMP configurations, the deferred floating point switch is not 
supported
  * in principle.  So, use here a synchronous floating point switching.
  * Synchronous means that the volatile floating point context is saved and
  * restored around a thread dispatch issued during interrupt 
processing.  Thus
  * post-switch actions and context switch extensions may safely use the
  * floating point unit.
  */
#if SPARC_HAS_FPU == 1
   #if defined(RTEMS_SMP)
     #define SPARC_USE_SYNCHRONOUS_FP_SWITCH
   #else
     #define SPARC_USE_LAZY_FP_SWITCH
   #endif
#endif

For simplicity, we disabled this optimization in SMP configurations. It 
could be probably used also in SMP configurations, however, this is a 
bit more complicated if threads move to other cores.

> because we are now building RTEMS with V enabled, we 
> need to add this save/restore to the CPU_Interrupt_frame and incur that 
> cost on all interrupts and stack space for all tasks.   (A small 
> secondary effect is that CPU_INTERRUPT_FRAME_SIZE is now larger than the 
> immediate addressing range).
> 
> Is there an alternative to consider?   Might one build RTEMS itself 
> without V, leaving that only for Applications/Tasks, perhaps adding a 
> Task attribute, and thereby taking the vector save/restore penalty only 
> when switching into or out of a vector enabled task?  (Perhaps similar 
> to HW floating point from the past).  One would then use an RTEMS config 
> flag to enable vector support, although qualified in runtime by 
> validating the existence of the vector ISA (misa CSR).    Or maybe that 
> direction would require too many changes?  e.g I see that “is_fp” is 
> used more specifically, rather than the more general rtems_attribute in 
> Thread_Control and in the arguments to _Context_Initialize().  Anyways, 
> I’m happy to hear any thoughts on this subject.  Note that I’m new to 
> RTEMS, so may not have some past context.

The potential optimizations depend also on how aggressively the vector 
unit is used by the compiler. On powerpc for example, the AltiVec unit 
is also used to clear or copy non-vector data. There is also a vrsave 
register, which helps to optimize the AltiVec context save/restore. If 
you disable the vector unit for certain jobs (like interrupt handlers, 
non-vector tasks), then you have to ensure that the compiler doesn't 
generate vector unit instructions. In theory, you could compile your 
code with different options. However, during linking you provably have 
to select the non-vector standard libraries. Specialized numeric 
libraries may have to be restricted to vector tasks.

 From an RTEMS API point of view, we could simply use the 
RTEMS_FLOATING_POINT attribute. This is a bit of an issue since POSIX 
threads use this by default. Maybe it makes sense to introduce a 
non-standard attribute for POSIX threads to be control this.

-- 
embedded brains GmbH & Co. KG
Herr Sebastian HUBER
Dornierstr. 4
82178 Puchheim
Germany
email: sebastian.huber at embedded-brains.de
phone: +49-89-18 94 741 - 16
fax:   +49-89-18 94 741 - 08

Registergericht: Amtsgericht München
Registernummer: HRB 157899
Vertretungsberechtigte Geschäftsführer: Peter Rasmussen, Thomas Dörfler
Unsere Datenschutzerklärung finden Sie hier:
https://embedded-brains.de/datenschutzerklaerung/


More information about the devel mailing list