/**
* @file rtems/score/cpu.h
*/
/*
*
* Copyright (c) 2015 University of York.
* Hesham Almatary <hesham@alumni.york.ac.uk>
*
* COPYRIGHT (c) 1989-1999.
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _RISCV_CPU_H
#define _RISCV_CPU_H
#ifdef __cplusplus
extern "C" {
#endif
#include <rtems/score/basedefs.h>
#include <rtems/score/riscv.h> /* pick up machine definitions */
#include <rtems/score/riscv-utility.h>
#ifndef ASM
#include <rtems/bspIo.h>
#include <stdint.h>
#include <stdio.h> /* for printk */
#endif
#define CPU_INLINE_ENABLE_DISPATCH FALSE
#define CPU_UNROLL_ENQUEUE_PRIORITY TRUE
#define CPU_ISR_PASSES_FRAME_POINTER 1
#define CPU_HARDWARE_FP FALSE
#define CPU_SOFTWARE_FP FALSE
#define CPU_ALL_TASKS_ARE_FP FALSE
#define CPU_IDLE_TASK_IS_FP FALSE
#define CPU_USE_DEFERRED_FP_SWITCH FALSE
#define CPU_PROVIDES_IDLE_THREAD_BODY TRUE
#define CPU_STACK_GROWS_UP FALSE
#define CPU_STRUCTURE_ALIGNMENT __attribute__ ((aligned (64)))
#define CPU_HAS_OWN_HOST_TO_NETWORK_ROUTINES FALSE
#define CPU_BIG_ENDIAN FALSE
#define CPU_LITTLE_ENDIAN TRUE
#define CPU_MODES_INTERRUPT_MASK 0x0000000000000001
/*
* Processor defined structures required for cpukit/score.
*/
#ifndef ASM
typedef struct {
/* riscv has 32 xlen-bit (where xlen can be 32 or 64) general purpose registers (x0-x31)*/
unsigned long x[32];
/* Special purpose registers */
unsigned long mstatus;
unsigned long mcause;
unsigned long mepc;
#ifdef RTEMS_SMP
/**
* @brief On SMP configurations the thread context must contain a boolean
* indicator to signal if this context is executing on a processor.
*
* This field must be updated during a context switch. The context switch
* to the heir must wait until the heir context indicates that it is no
* longer executing on a processor. The context switch must also check if
* a thread dispatch is necessary to honor updates of the heir thread for
* this processor. This indicator must be updated using an atomic test and
* set operation to ensure that at most one processor uses the heir
* context at the same time.
*
* @code
* void _CPU_Context_switch(
* Context_Control *executing,
* Context_Control *heir
* )
* {
* save( executing );
*
* executing->is_executing = false;
* memory_barrier();
*
* if ( test_and_set( &heir->is_executing ) ) {
* do {
* Per_CPU_Control *cpu_self = _Per_CPU_Get_snapshot();
*
* if ( cpu_self->dispatch_necessary ) {
* heir = _Thread_Get_heir_and_make_it_executing( cpu_self );
* }
* } while ( test_and_set( &heir->is_executing ) );
* }
*
* restore( heir );
* }
* @endcode
*/
volatile bool is_executing;
#endif
} Context_Control;
#define _CPU_Context_Get_SP( _context ) \
(_context)->x[2]
typedef struct {
/** TODO FPU registers are listed here */
double some_float_register;
} Context_Control_fp;
typedef Context_Control CPU_Interrupt_frame;
#define CPU_CONTEXT_FP_SIZE 0
Context_Control_fp _CPU_Null_fp_context;
#define CPU_CACHE_LINE_BYTES 64
#define CPU_MPCI_RECEIVE_SERVER_EXTRA_STACK 0
#if __riscv_xlen == 32
#define CPU_STACK_MINIMUM_SIZE 4096
#else
#define CPU_STACK_MINIMUM_SIZE 4096 * 2
#endif
#define CPU_ALIGNMENT 8
#define CPU_PROVIDES_ISR_IS_IN_PROGRESS FALSE
#define CPU_HEAP_ALIGNMENT CPU_ALIGNMENT
#define CPU_PARTITION_ALIGNMENT CPU_ALIGNMENT
#define CPU_STACK_ALIGNMENT 8
#define CPU_INTERRUPT_STACK_ALIGNMENT CPU_CACHE_LINE_BYTES
#define _CPU_Initialize_vectors()
/*
* Disable all interrupts for an RTEMS critical section. The previous
* level is returned in _level.
*
*/
static inline unsigned long riscv_interrupt_disable( void )
{
unsigned long status = read_csr(mstatus);
clear_csr(mstatus, MSTATUS_MIE);
return status;
}
static inline void riscv_interrupt_enable(unsigned long level)
{
write_csr(mstatus, level);
}
#define _CPU_ISR_Disable( _level ) \
_level = riscv_interrupt_disable()
#define _CPU_ISR_Enable( _level ) \
riscv_interrupt_enable( _level )
#define _CPU_ISR_Flash( _level ) \
do{ \
_CPU_ISR_Enable( _level ); \
riscv_interrupt_disable(); \
} while(0)
RTEMS_INLINE_ROUTINE bool _CPU_ISR_Is_enabled( unsigned long level )
{
return ( level & MSTATUS_MIE ) != 0;
}
void _CPU_ISR_Set_level( unsigned long level );
unsigned long _CPU_ISR_Get_level( void );
/* end of ISR handler macros */
/* Context handler macros */
#define RISCV_GCC_RED_ZONE_SIZE 128
void _CPU_Context_Initialize(
Context_Control *context,
void *stack_area_begin,
size_t stack_area_size,
unsigned long new_level,
void (*entry_point)( void ),
bool is_fp,
void *tls_area
);
#define _CPU_Context_Restart_self( _the_context ) \
_CPU_Context_restore( (_the_context) )
#define _CPU_Context_Fp_start( _base, _offset ) \
( (void *) _Addresses_Add_offset( (_base), (_offset) ) )
#define _CPU_Context_Initialize_fp( _destination ) \
{ \
*(*(_destination)) = _CPU_Null_fp_context; \
}
extern void _CPU_Fatal_halt(uint32_t source, uint32_t error) RTEMS_NO_RETURN;
/* end of Fatal Error manager macros */
#define CPU_USE_GENERIC_BITFIELD_CODE TRUE
#define CPU_USE_GENERIC_BITFIELD_DATA TRUE
#if (CPU_USE_GENERIC_BITFIELD_CODE == FALSE)
#define _CPU_Bitfield_Find_first_bit( _value, _output ) \
{ \
(_output) = 0; /* do something to prevent warnings */ \
}
#endif
/* end of Bitfield handler macros */
/*
* This routine builds the mask which corresponds to the bit fields
* as searched by _CPU_Bitfield_Find_first_bit(). See the discussion
* for that routine.
*
*/
#if (CPU_USE_GENERIC_BITFIELD_CODE == FALSE)
#define _CPU_Priority_Mask( _bit_number ) \
(1 << _bit_number)
#endif
#if (CPU_USE_GENERIC_BITFIELD_CODE == FALSE)
#define _CPU_Priority_bits_index( _priority ) \
(_priority)
#endif
#define CPU_MAXIMUM_PROCESSORS 32
#define CPU_TIMESTAMP_USE_STRUCT_TIMESPEC FALSE
#define CPU_TIMESTAMP_USE_INT64 TRUE
#define CPU_TIMESTAMP_USE_INT64_INLINE FALSE
typedef struct {
/* There is no CPU specific per-CPU state */
} CPU_Per_CPU_control;
#endif /* ASM */
#if __riscv_xlen == 32
#define CPU_SIZEOF_POINTER 4
/* 32-bit load/store instructions */
#define LREG lw
#define SREG sw
#define CPU_EXCEPTION_FRAME_SIZE 128
#else /* xlen = 64 */
#define CPU_SIZEOF_POINTER 8
/* 64-bit load/store instructions */
#define LREG ld
#define SREG sd
#define CPU_EXCEPTION_FRAME_SIZE 256
#endif
#define CPU_PER_CPU_CONTROL_SIZE 0
#ifndef ASM
typedef uint16_t Priority_bit_map_Word;
typedef struct {
unsigned long x[32];;
} CPU_Exception_frame;
/**
* @brief Prints the exception frame via printk().
*
* @see rtems_fatal() and RTEMS_FATAL_SOURCE_EXCEPTION.
*/
void _CPU_Exception_frame_print( const CPU_Exception_frame *frame );
/* end of Priority handler macros */
/* functions */
/*
* _CPU_Initialize
*
* This routine performs CPU dependent initialization.
*
*/
void _CPU_Initialize(
void
);
/*
* _CPU_ISR_install_raw_handler
*
* This routine installs a "raw" interrupt handler directly into the
* processor's vector table.
*
*/
void _CPU_ISR_install_raw_handler(
uint32_t vector,
proc_ptr new_handler,
proc_ptr *old_handler
);
/*
* _CPU_ISR_install_vector
*
* This routine installs an interrupt vector.
*
* NO_CPU Specific Information:
*
* XXX document implementation including references if appropriate
*/
void _CPU_ISR_install_vector(
unsigned long vector,
proc_ptr new_handler,
proc_ptr *old_handler
);
/*
* _CPU_Thread_Idle_body
*
* This routine is the CPU dependent IDLE thread body.
*
* NOTE: It need only be provided if CPU_PROVIDES_IDLE_THREAD_BODY
* is TRUE.
*
*/
void *_CPU_Thread_Idle_body( uintptr_t ignored );
/*
* _CPU_Context_switch
*
* This routine switches from the run context to the heir context.
*
* RISCV Specific Information:
*
* Please see the comments in the .c file for a description of how
* this function works. There are several things to be aware of.
*/
void _CPU_Context_switch(
Context_Control *run,
Context_Control *heir
);
/*
* _CPU_Context_restore
*
* This routine is generally used only to restart self in an
* efficient manner. It may simply be a label in _CPU_Context_switch.
*
* NOTE: May be unnecessary to reload some registers.
*
*/
void _CPU_Context_restore(
Context_Control *new_context
) RTEMS_NO_RETURN;
/*
* _CPU_Context_save_fp
*
* This routine saves the floating point context passed to it.
*
*/
void _CPU_Context_save_fp(
void **fp_context_ptr
);
/*
* _CPU_Context_restore_fp
*
* This routine restores the floating point context passed to it.
*
*/
void _CPU_Context_restore_fp(
void **fp_context_ptr
);
/* The following routine swaps the endian format of an unsigned int.
* It must be static because it is referenced indirectly.
*
* This version will work on any processor, but if there is a better
* way for your CPU PLEASE use it. The most common way to do this is to:
*
* swap least significant two bytes with 16-bit rotate
* swap upper and lower 16-bits
* swap most significant two bytes with 16-bit rotate
*
* Some CPUs have special instructions which swap a 32-bit quantity in
* a single instruction (e.g. i486). It is probably best to avoid
* an "endian swapping control bit" in the CPU. One good reason is
* that interrupts would probably have to be disabled to insure that
* an interrupt does not try to access the same "chunk" with the wrong
* endian. Another good reason is that on some CPUs, the endian bit
* endianness for ALL fetches -- both code and data -- so the code
* will be fetched incorrectly.
*
*/
static inline uint32_t CPU_swap_u32(
uint32_t value
)
{
uint32_t byte1, byte2, byte3, byte4, swapped;
byte4 = (value >> 24) & 0xff;
byte3 = (value >> 16) & 0xff;
byte2 = (value >> 8) & 0xff;
byte1 = value & 0xff;
swapped = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
return ( swapped );
}
#define CPU_swap_u16( value ) \
(((value&0xff) << 8) | ((value >> 8)&0xff))
static inline void _CPU_Context_volatile_clobber( uintptr_t pattern )
{
/* TODO */
}
static inline void _CPU_Context_validate( uintptr_t pattern )
{
while (1) {
/* TODO */
}
}
typedef uint32_t CPU_Counter_ticks;
uint32_t _CPU_Counter_frequency( void );
CPU_Counter_ticks _CPU_Counter_read( void );
#ifdef RTEMS_SMP
/**
* @brief Performs CPU specific SMP initialization in the context of the boot
* processor.
*
* This function is invoked on the boot processor during system
* initialization. All interrupt stacks are allocated at this point in case
* the CPU port allocates the interrupt stacks. This function is called
* before _CPU_SMP_Start_processor() or _CPU_SMP_Finalize_initialization() is
* used.
*
* @return The count of physically or virtually available processors.
* Depending on the configuration the application may use not all processors.
*/
uint32_t _CPU_SMP_Initialize( void );
/**
* @brief Starts a processor specified by its index.
*
* This function is invoked on the boot processor during system
* initialization.
*
* This function will be called after _CPU_SMP_Initialize().
*
* @param[in] cpu_index The processor index.
*
* @retval true Successful operation.
* @retval false Unable to start this processor.
*/
bool _CPU_SMP_Start_processor( uint32_t cpu_index );
/**
* @brief Performs final steps of CPU specific SMP initialization in the
* context of the boot processor.
*
* This function is invoked on the boot processor during system
* initialization.
*
* This function will be called after all processors requested by the
* application have been started.
*
* @param[in] cpu_count The minimum value of the count of processors
* requested by the application configuration and the count of physically or
* virtually available processors.
*/
void _CPU_SMP_Finalize_initialization( uint32_t cpu_count );
/**
* @brief Returns the index of the current processor.
*
* An architecture specific method must be used to obtain the index of the
* current processor in the system. The set of processor indices is the
* range of integers starting with zero up to the processor count minus one.
*/
uint32_t _CPU_SMP_Get_current_processor( void );
/**
* @brief Sends an inter-processor interrupt to the specified target
* processor.
*
* This operation is undefined for target processor indices out of range.
*
* @param[in] target_processor_index The target processor index.
*/
void _CPU_SMP_Send_interrupt( uint32_t target_processor_index );
/**
* @brief Broadcasts a processor event.
*
* Some architectures provide a low-level synchronization primitive for
* processors in a multi-processor environment. Processors waiting for this
* event may go into a low-power state and stop generating system bus
* transactions. This function must ensure that preceding store operations
* can be observed by other processors.
*
* @see _CPU_SMP_Processor_event_receive().
*/
void _CPU_SMP_Processor_event_broadcast( void );
/**
* @brief Receives a processor event.
*
* This function will wait for the processor event and may wait forever if no
* such event arrives.
*
* @see _CPU_SMP_Processor_event_broadcast().
*/
static inline void _CPU_SMP_Processor_event_receive( void )
{
__asm__ volatile ( "" : : : "memory" );
}
/**
* @brief Gets the is executing indicator of the thread context.
*
* @param[in] context The context.
*/
static inline bool _CPU_Context_Get_is_executing(
const Context_Control *context
)
{
return context->is_executing;
}
/**
* @brief Sets the is executing indicator of the thread context.
*
* @param[in] context The context.
* @param[in] is_executing The new value for the is executing indicator.
*/
static inline void _CPU_Context_Set_is_executing(
Context_Control *context,
bool is_executing
)
{
context->is_executing = is_executing;
}
#endif /* RTEMS_SMP */
/** Type that can store a 32-bit integer or a pointer. */
typedef uintptr_t CPU_Uint32ptr;
#endif /* ASM */
#ifdef __cplusplus
}
#endif
#endif