Commit c3353c3c authored by Andreas Werner's avatar Andreas Werner
Browse files

Risc-V: Add Risc-V Arch Port

parent 476fdc71
......@@ -17,6 +17,10 @@ choice
bool "Frama-C"
help
Frama-C Verfication Architecture
config ARCH_RISCV
bool "RISC-V"
help
RISC-V Based Architecture
endchoice
comment "Global Settings"
comment "Waring CPU Clock Hz not set!"
......@@ -50,4 +54,7 @@ endif
if ARCH_C2000=y
source arch/c2000/Kconfig
endif
if ARCH_RISCV=y
source arch/riscv/Kconfig
endif
endmenu
Copyright (c) 2020 Andreas Werner <kernel@andy89.org>
Copyright (c) 2021 Andreas Werner <kernel@andy89.org>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
......
obj-$(CONFIG_ARCH_ARM)+=arm/
obj-$(CONFIG_ARCH_X86)+=x86/
obj-$(CONFIG_ARCH_C2000)+=c2000/
obj-$(CONFIG_ARCH_RISCV)+=riscv/
obj-$(CONFIG_GEN_VERSION) += version.o
BUILDID := $(USER)@$(shell hostname) $(shell date +'%Y-%m-%d %H:%M:%S')
......
# Slected by Mach
choice
prompt "Select Mode"
config ARCH_RISCV_MMODE
bool "Machine-mode / User-mode"
# No Port Available
#config ARCH_RISCV_SMODE
# bool "Supervisor-mode / User-mode"
endchoice
if ARCH_RISCV_MMODE=y
source arch/riscv/mmode/Kconfig
endif
config ARCH_CFLAGS
string
default "-mcmodel=medany"
config ARCH_AFLAGS
string
default "-mcmodel=medany"
config ARCH_LDFLAGS
string
default ""
config EXTRA_ARCH_LINKFLAGS
string
default "-nostartfiles"
config ARCH_HAS_OPTIMISED_TASK_SELECTION
bool
default y
config ARCH_RISCV_PLIC
bool
depends on MACH_HAS_PLIC
default y
config ARCH_RISCV_HAS_MTIME
bool
depends on MACH_HAS_MTIME
default y
config ARCH_RISCV_SEP_IRQ_STACK
bool "Separated IRQ Stack"
default n
help
Separated IRQ Stack if not defined Main Stack is used
config ARCH_RISCV_ISR_STACK_SIZE_WORDS
int "ISR STACK SIZE WORDS"
depends on ARCH_RISCV_SEP_IRQ_STACK
default 500
config ARCH_RISCV_HAS_CLINT
bool
default y
depends on MACH_HAS_CLINT
config INCLUDE_ARCH_FREEROTS_CONFIG
bool
default y
config ARCH_RISCV_INIT
bool
default y
depends on MACH_NEED_RISCV_INIT
obj-$(CONFIG_ARCH_RISCV_MMODE) += mmode/
obj-$(CONFIG_ARCH_RISCV_PLIC) += plic/
obj-$(CONFIG_ARCH_RISCV_INIT) += init.o
subdir-asflags-y += -include arch/riscv/include/FreeROTSArchConfig.h
/* SPDX-License-Identifier: MIT */
/*
* Author: Andreas Werner <kernel@andy89.org>
* Date: 2021
*/
#ifndef FREEROTS_ARCH_CONFIG_H_
#define FREEROTS_ARCH_CONFIG_H_
#define portasmHANDLE_INTERRUPT irq_handler
#ifdef CONFIG_ARCH_RISCV_HAS_MTIME
# define portasmHAS_MTIME 1
# define configMTIME_BASE_ADDRESS CONFIG_MACH_MTIME_BASE_ADDRESS
# define configMTIMECMP_BASE_ADDRESS CONFIG_MACH_MTIMECMP_BASE_ADDRESS
#endif
#ifdef CONFIG_ARCH_RISCV_SEP_IRQ_STACK
# define configISR_STACK_SIZE_WORDS CONFIG_ARCH_RISCV_ISR_STACK_SIZE_WORDS
#endif
#ifdef CONFIG_ARCH_RISCV_HAS_CLINT
# define portasmHAS_SIFIVE_CLINT
#endif
#endif
/* SPDX-License-Identifier: MIT */
/*
* Author: Andreas Werner <kernel@andy89.org>
* Date: 2021
*/
#ifndef PLIC_H_
#define PLIC_H_
#include <stdint.h>
struct plic_irqTarget {
uint32_t threshold;
uint32_t claim_complete;
uint32_t reserved[1022];
};
struct plic_enable {
uint32_t enables[32];
};
struct plic {
uint32_t prio[1024];
uint32_t pending[32];
uint32_t reserved[992];
struct plic_enable targetEnable[15808];
uint32_t reserved2[16384];
struct plic_irqTarget target[15872];
};
typedef void (*vector_table_entry_t)(void);
extern vector_table_entry_t vector[CONFIG_MACH_IRQ_COUNT];
extern volatile struct plic *PLIC;
#endif
/* SPDX-License-Identifier: MIT */
/*
* Author: Andreas Werner <kernel@andy89.org>
* Date: 2021
*/
#ifndef RISC_V_
#define RISC_V_
#include <stdint.h>
#if __riscv_xlen == 64
# define MCAUSE_CAUSE 0x7FFFFFFFFFFFFFFF
# define MCAUSE_INT 0x8000000000000000
#else
# define MCAUSE_CAUSE 0x7FFFFFFF
# define MCAUSE_INT 0x80000000
#endif
#define IRQ_S_SOFT 1
#define IRQ_H_SOFT 2
#define IRQ_M_SOFT 3
#define IRQ_S_TIMER 5
#define IRQ_H_TIMER 6
#define IRQ_M_TIMER 7
#define IRQ_S_EXT 9
#define IRQ_H_EXT 10
#define IRQ_M_EXT 11
#define read_csr(reg) ({ \
unsigned long __tmp; \
asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
__tmp; \
})
#define write_csr(reg, val) ({ \
if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
asm volatile ("csrw " #reg ", %0" :: "i"(val)); \
else \
asm volatile ("csrw " #reg ", %0" :: "r"(val)); })
#define swap_csr(reg, val) ({ unsigned long __tmp; \
if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "i"(val)); \
else \
asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "r"(val)); \
__tmp; })
#define set_csr(reg, bit) ({ unsigned long __tmp; \
if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
else \
asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
__tmp; })
#define clear_csr(reg, bit) ({ unsigned long __tmp; \
if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \
else \
asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \
__tmp; })
#endif
/* SPDX-License-Identifier: MIT */
/*
* Author: Andreas Werner <kernel@andy89.org>
* Date: 2021
*/
.extern _data_table
.extern _data_table_end
.extern __bss_start__
.extern __bss_end__
/* Define LOAD and STOR */
#if __riscv_xlen == 32
#define REGSIZE 4
#define REGSHIFT 2
#define LOAD lw
#define STOR sw
#elif __riscv_xlen == 64
#define REGSIZE 8
#define REGSHIFT 3
#define LOAD ld
#define STOR sd
#endif
.section .text
.globl datainit
.type datainit,@function
/* a0 data_table pointer */
/* a1 data_table_end */
/* a2 src pointer */
/* a3 dst pointer */
/* a4 len Length*/
/* t0 data */
/* t1 i */
datainit:
la a0, _data_table
la a1, _data_table_end
1:
LOAD a2, (a0) /* src */
addi a0, a0, REGSIZE
LOAD a3, (a0) /* dst */
addi a0, a0, REGSIZE
LOAD a4, (a0) /* len */
addi a0, a0, REGSIZE
mv t1, zero /* init i to 0 */
j 3f
2:
LOAD t0, (a2)
STOR t0, (a3)
addi a2, a2, REGSIZE
addi a3, a3, REGSIZE
addi t1, t1, REGSIZE
3:
bltu t1, a4, 2b /* if more data is to copy */
bltu a0, a1, 1b /* if there more in data table */
ret
.section .text
.globl bssinit
.type bssinit,@function
bssinit:
la a0, __bss_start__
la a1, __bss_end__
bgeu a0, a1, 2f
1:
STOR zero, (a0)
addi a0, a0, REGSIZE
bltu a0, a1, 1b
2:
ret
config ARCH_INCLUDE
string
default "-Iarch/riscv/mmode -Iarch/riscv/include"
obj-y += port.o portASM.o port.o
/*
* FreeRTOS Kernel V10.4.3
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
* 1 tab == 4 spaces!
*/
/*-----------------------------------------------------------
* Implementation of functions defined in portable.h for the RISC-V RV32 port.
*----------------------------------------------------------*/
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "portmacro.h"
/* Standard includes. */
#include "string.h"
#ifdef configCLINT_BASE_ADDRESS
#warning The configCLINT_BASE_ADDRESS constant has been deprecated. configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS are currently being derived from the (possibly 0) configCLINT_BASE_ADDRESS setting. Please update to define configMTIME_BASE_ADDRESS and configMTIMECMP_BASE_ADDRESS dirctly in place of configCLINT_BASE_ADDRESS. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
#ifndef configMTIME_BASE_ADDRESS
#warning configMTIME_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtime register then set configMTIME_BASE_ADDRESS to the mapped address. Otherwise set configMTIME_BASE_ADDRESS to 0. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
#ifndef configMTIMECMP_BASE_ADDRESS
#warning configMTIMECMP_BASE_ADDRESS must be defined in FreeRTOSConfig.h. If the target chip includes a memory-mapped mtimecmp register then set configMTIMECMP_BASE_ADDRESS to the mapped address. Otherwise set configMTIMECMP_BASE_ADDRESS to 0. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
/* Let the user override the pre-loading of the initial LR with the address of
prvTaskExitError() in case it messes up unwinding of the stack in the
debugger. */
#ifdef configTASK_RETURN_ADDRESS
#define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
#else
#define portTASK_RETURN_ADDRESS prvTaskExitError
#endif
/* The stack used by interrupt service routines. Set configISR_STACK_SIZE_WORDS
to use a statically allocated array as the interrupt stack. Alternative leave
configISR_STACK_SIZE_WORDS undefined and update the linker script so that a
linker variable names __freertos_irq_stack_top has the same value as the top
of the stack used by main. Using the linker script method will repurpose the
stack that was used by main before the scheduler was started for use as the
interrupt stack after the scheduler has started. */
#ifdef configISR_STACK_SIZE_WORDS
static __attribute__ ((aligned(16))) StackType_t xISRStack[ configISR_STACK_SIZE_WORDS ] = { 0 };
const StackType_t xISRStackTop = ( StackType_t ) &( xISRStack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] );
/* Don't use 0xa5 as the stack fill bytes as that is used by the kernerl for
the task stacks, and so will legitimately appear in many positions within
the ISR stack. */
#define portISR_STACK_FILL_BYTE 0xee
#else
extern const uint32_t __freertos_irq_stack_top[];
const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top;
#endif
/*
* Setup the timer to generate the tick interrupts. The implementation in this
* file is weak to allow application writers to change the timer used to
* generate the tick interrupt.
*/
void vPortSetupTimerInterrupt( void ) __attribute__(( weak ));
/*-----------------------------------------------------------*/
/* Used to program the machine timer compare register. */
uint64_t ullNextTime = 0ULL;
const uint64_t *pullNextTime = &ullNextTime;
size_t uxTimerIncrementsForOneTick; /* Assumes increment won't go over 32-bits. */
uint32_t const ullMachineTimerCompareRegisterBase = configMTIMECMP_BASE_ADDRESS;
volatile uint64_t * pullMachineTimerCompareRegister = NULL;
/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task
stack checking. A problem in the ISR stack will trigger an assert, not call the
stack overflow hook function (because the stack overflow hook is specific to a
task stack, not the ISR stack). */
#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 )
#warning This path not tested, or even compiled yet.
static const uint8_t ucExpectedStackBytes[] = {
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE }; \
#define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) )
#else
/* Define the function away. */
#define portCHECK_ISR_STACK()
#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
/*-----------------------------------------------------------*/
#if( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 )
void vPortSetupTimerInterrupt( void )
{
uint32_t ulCurrentTimeHigh, ulCurrentTimeLow;
volatile uint32_t * const pulTimeHigh = ( volatile uint32_t * const ) ( ( configMTIME_BASE_ADDRESS ) + 4UL ); /* 8-byte typer so high 32-bit word is 4 bytes up. */
volatile uint32_t * const pulTimeLow = ( volatile uint32_t * const ) ( configMTIME_BASE_ADDRESS );
volatile uint32_t ulHartId;
__asm volatile( "csrr %0, mhartid" : "=r"( ulHartId ) );
pullMachineTimerCompareRegister = ( volatile uint64_t * ) ( ullMachineTimerCompareRegisterBase + ( ulHartId * sizeof( uint64_t ) ) );
do
{
ulCurrentTimeHigh = *pulTimeHigh;
ulCurrentTimeLow = *pulTimeLow;
} while( ulCurrentTimeHigh != *pulTimeHigh );
ullNextTime = ( uint64_t ) ulCurrentTimeHigh;
ullNextTime <<= 32ULL; /* High 4-byte word is 32-bits up. */
ullNextTime |= ( uint64_t ) ulCurrentTimeLow;
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
*pullMachineTimerCompareRegister = ullNextTime;
/* Prepare the time to use after the next tick interrupt. */
ullNextTime += ( uint64_t ) uxTimerIncrementsForOneTick;
}
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIME_BASE_ADDRESS != 0 ) */
/*-----------------------------------------------------------*/
BaseType_t xPortStartScheduler( void )
{
extern void xPortStartFirstTask( void );
uxTimerIncrementsForOneTick = ( size_t ) ( ( configCPU_CLOCK_HZ ) / ( configTICK_RATE_HZ ) ); /* init here to handle configCPU_CLOCK_HZ by clock API */
#if( configASSERT_DEFINED == 1 )
{
volatile uint32_t mtvec = 0;
/* Check the least significant two bits of mtvec are 00 - indicating
single vector mode. */
__asm volatile( "csrr %0, mtvec" : "=r"( mtvec ) );
configASSERT( ( mtvec & 0x03UL ) == 0 );
/* Check alignment of the interrupt stack - which is the same as the
stack that was being used by main() prior to the scheduler being
started. */
configASSERT( ( xISRStackTop & portBYTE_ALIGNMENT_MASK ) == 0 );
#ifdef configISR_STACK_SIZE_WORDS
{
memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) );
}
#endif /* configISR_STACK_SIZE_WORDS */
}
#endif /* configASSERT_DEFINED */
/* If there is a CLINT then it is ok to use the default implementation
in this file, otherwise vPortSetupTimerInterrupt() must be implemented to
configure whichever clock is to be used to generate the tick interrupt. */
vPortSetupTimerInterrupt();
#if( ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) )
{
/* Enable mtime and external interrupts. 1<<7 for timer interrupt, 1<<11
for external interrupt. _RB_ What happens here when mtime is not present as
with pulpino? */
__asm volatile( "csrs mie, %0" :: "r"(0x880) );
}
#else
{
/* Enable external interrupts. */
__asm volatile( "csrs mie, %0" :: "r"(0x800) );
}
#endif /* ( configMTIME_BASE_ADDRESS != 0 ) && ( configMTIMECMP_BASE_ADDRESS != 0 ) */
xPortStartFirstTask();
/* Should not get here as after calling xPortStartFirstTask() only tasks
should be executing. */
return pdFAIL;
}
/*-----------------------------------------------------------*/
void vPortEndScheduler( void )
{
/* Not implemented. */
for( ;; );
}
/*
* FreeRTOS Kernel V10.4.3
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
* 1 tab == 4 spaces!
*/
/*
* The FreeRTOS kernel's RISC-V port is split between the the code that is
* common across all currently supported RISC-V chips (implementations of the
* RISC-V ISA), and code which tailors the port to a specific RISC-V chip:
*
* + The code that is common to all RISC-V chips is implemented in
* FreeRTOS\Source\portable\GCC\RISC-V-RV32\portASM.S. There is only one
* portASM.S file because the same file is used no matter which RISC-V chip is
* in use.
*
* + The code that tailors the kernel's RISC-V port to a specific RISC-V
* chip is implemented in freertos_risc_v_chip_specific_extensions.h. There
* is one freertos_risc_v_chip_specific_extensions.h that can be used with any
* RISC-V chip that both includes a standard CLINT and does not add to the
* base set of RISC-V registers. There are additional
* freertos_risc_v_chip_specific_extensions.h files for RISC-V implementations
* that do not include a standard CLINT or do add to the base set of RISC-V
* registers.
*
* CARE MUST BE TAKEN TO INCLDUE THE CORRECT
* freertos_risc_v_chip_specific_extensions.h HEADER FILE FOR THE CHIP
* IN USE. To include the correct freertos_risc_v_chip_specific_extensions.h
* header file ensure the path to the correct header file is in the assembler's
* include path.
*
* This freertos_risc_v_chip_specific_extensions.h is for use on RISC-V chips
* that include a standard CLINT and do not add to the base set of RISC-V
* registers.
*
*/
#if __riscv_xlen == 64
#define portWORD_SIZE 8
#define store_x sd
#define load_x ld
#elif __riscv_xlen == 32
#define store_x sw
#define load_x lw
#define portWORD_SIZE 4
#else
#error Assembler did not define __riscv_xlen
#endif
#include "freertos_risc_v_chip_specific_extensions.h"
/* Check the freertos_risc_v_chip_specific_extensions.h and/or command line
definitions. */
#if defined( portasmHAS_CLINT ) && defined( portasmHAS_MTIME )
#error The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME. portasmHAS_CLINT and portasmHAS_MTIME cannot both be defined at once. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
#ifdef portasmHAS_CLINT
#warning The portasmHAS_CLINT constant has been deprecated. Please replace it with portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT. For now portasmHAS_MTIME and portasmHAS_SIFIVE_CLINT are derived from portasmHAS_CLINT. See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#define portasmHAS_MTIME portasmHAS_CLINT
#define portasmHAS_SIFIVE_CLINT portasmHAS_CLINT
#endif
#ifndef portasmHAS_MTIME
#error freertos_risc_v_chip_specific_extensions.h must define portasmHAS_MTIME to either 1 (MTIME clock present) or 0 (MTIME clock not present). See https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
#ifndef portasmHANDLE_INTERRUPT
#error portasmHANDLE_INTERRUPT must be defined to the function to be called to handle external/peripheral interrupts. portasmHANDLE_INTERRUPT can be defined on the assembler command line or in the appropriate freertos_risc_v_chip_specific_extensions.h header file. https://www.FreeRTOS.org/Using-FreeRTOS-on-RISC-V.html
#endif
#ifndef portasmHAS_SIFIVE_CLINT
#define portasmHAS_SIFIVE_CLINT 0
#endif
/* Only the standard core registers are stored by default. Any additional
registers must be saved by the portasmSAVE_ADDITIONAL_REGISTERS and
portasmRESTORE_ADDITIONAL_REGISTERS macros - which can be defined in a chip
specific version of freertos_risc_v_chip_specific_extensions.h. See the notes
at the top of this file. */
#define portCONTEXT_SIZE ( 30 * portWORD_SIZE )
.global xPortStartFirstTask