新ちゃん 发表于 2020-8-16 23:11:28

[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers

本帖最后由 新ちゃん 于 2020-8-21 03:42 编辑

FreeRTOS 10.2.1版本开始,官方加入risc-v demo,并且增加目录 source/portable/gcc/risc-v .
正式官宣对risc-v soc的支持,改动部分可以参考以下地址:https://www.freertos.org/Using-FreeRTOS-on-RISC-V.html
实际上对于risc-v soc的使用,很多用户早就可以运行于FreeRTOS上了,之前用的就是在github上搜到非官方的接口文件(port)。
这次官方应该也是看到多个soc上运行的FreeRTOS,把它们进行了整合发布出来。
demo目录下共有3个soc,分别是
[*]MiFive M2GL025 Creative Board and Renode using GCC and the SoftConsole IDE
[*]VEGAboard PULP RI5CY Demo using GCC and Eclipse
[*]SiFive sifive_e QEMU emulator using Freedom Studio and GCC


三家公司代表了市面上主流的商业ip
Microchip和Microsemi 合并以后既有FPGA又有board,它这块板子上soc并不清楚使用了什么core,但从risc-v官网上看micorchip 开源贡献里使用的都是rocket


第二个搜了一下是NXP的RV32M1,用了4个core,2个risc-v的,2个ARM的,RISC-V的core是RI5CY和zero RI5CY.
ETH Zurich 是瑞士的苏黎世联邦理工, 而Università di Bologna是意大利的博洛尼亚大学
   第三个就是更具有代表性的risc-v官方公司sifive了,具体的信息并不清楚。
该文档主要阐述了针对risc-v的6处修改:

[*]Include the core FreeRTOS source files and the FreeRTOS RISC-V port layer source files in your project.
[*]Ensure the assembler’s include path includes the path to the header file that describes any chip specific implementation details.
[*]Define either a constant in FreeRTOSConfig.h or a linker variable to specify the memory to use as the interrupt stack.
[*]Define configCLINT_BASE_ADDRESS in FreeRTOSConfig.h.
[*]For the assembler, #define portasmHANDLE_INTERRUPT to the name of the function provided by your chip or tools vendor for handling external interrupts.
[*]Install the FreeRTOS trap handler.


1/2,增加一个针对risc-v CPU的定义宏文件( freertos_risc_v_chip_specific_extensions.h ) ,实际当你使用某risc-v core时一定会有一个类似的定义文件:#define portasmHAS_CLINT 1a Core Local Interrupter (CLINT)   ,表示CPU是否有内部timer interrupt可用于tick;#define portasmADDITIONAL_CONTEXT_SIZE 0CPU有几个额外register当context switch时需要保存;#ifdef __ASSEMBLY__
.macro portasmSAVE_ADDITIONAL_REGISTERS
        /* No additional registers to save, so this macro does nothing. */
        .endm

/* Restore the additional registers found on the Pulpino. */
.macro portasmRESTORE_ADDITIONAL_REGISTERS
        /* No additional registers to restore, so this macro does nothing. */
        .endm
#endif
汇编宏用于context switch时save和load这些额外register;以及其他针对该CPU的设定,例如:scr1的IPIC register地址和置位宏就可以定义在此#define                                                                     IPIC_CISV                                        0xBF0
3,freertosconfig.h增加相关宏:如果CLINT=1,Timer地址定义在此,scr1的如下#define configCLINT_BASE_ADDRESS                0x4900004,修改了interrupt的stack使用,之前ISR使用的是task stack作为临时stack空间,新版本将独立出一个xISRStackTop空间。两种方法定义该空间,一是通过编译器分配数组的方式,二是ld里定义对应section symbol再赋值/* 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 ) - 1 ] );
#else
        extern const uint32_t __freertos_irq_stack_top[];
        const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top;
#endif
5,定义 宏portasmHANDLE_INTERRUPT作为ISR外部中断的入口。在官方的portASM.S中采用了非vector mode 的方式使用MTVEC register,而我自己使用的是scr1 的vector mode,所以并没有去使用这个宏 portasmHANDLE_INTERRUPT。
6,freertos_risc_v_trap_handler 标签标识exception/interrupt的入口地址,该地址要写入MTVEC中。对于scr1 vector mode,该地址是vector table的起始地址,每个exception和interrupt 都根据mcause * 4来作偏移。其中,中断包括software interrupt/timer interrupt /external interrupt,所有external interrupt共享一个入口地址,具体是哪个external interrupt要根据IPIC来判断。而ECALL指令产生的interrupt会使用exception 偏移地址11。所以相当于有两个software interrupt可用,一个被用于FreeRTOS 中 portYIELD()宏。Table 12: List of MCAUSE Exception Codes
INT   EC Description
0   0 Instruction address misaligned
0   1 Instruction access fault
0   2 Illegal instruction
0   3 Breakpoint
0   4 Load address misaligned
0   5 Load access fault
0   6 Store/AMO address misaligned
0   7 Store/AMO access fault
0   10..8 Not supported
0   11 Ecall from M-mode
0   >=12 Reserved

1   2..0 Reserved
1   3 Machine Software Interrupt
1   6..4 Reserved
1   7 Machine Timer Interrupt
1   10..8 Reserved
1   11 Machine External Interrupt
1   >=12 Reserved

新ちゃん 发表于 2020-8-16 23:14:01

本帖最后由 新ちゃん 于 2020-8-21 03:43 编辑

官宣里定义mstauts要保存在context switch中,用ecall呼叫实现portYIELD()。和之前用的function方式实现portYIELD()相比开销较多,自测会多花费30%的时间在context switch中。主要花费在CPU要跳转到freertos_risc_v_trap_handler并且 需要一次判断是非为ecall指令,再指向ISR stack空间。而保存进stack的数量几乎相当的。
/*
* Unlike other ports pxPortInitialiseStack() is written in assembly code as it
* needs access to the portasmADDITIONAL_CONTEXT_SIZE constant.The prototype
* for the function is as per the other ports:
* StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters );
*
* As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in
* a1, and pvParameters in a2.The new top of stack is passed out in a0.
*
* RISC-V maps registers to ABI names as follows (X1 to X31 integer registers
* for the 'I' profile, X1 to X15 for the 'E' profile, currently I assumed).
*
* Register                ABI Name        Description                                                Saver
* x0                        zero                Hard-wired zero                                        -
* x1                        ra                        Return address                                        Caller
* x2                        sp                        Stack pointer                                        Callee
* x3                        gp                        Global pointer                                        -
* x4                        tp                        Thread pointer                                        -
* x5-7                        t0-2                Temporaries                                                Caller
* x8                        s0/fp                Saved register/Frame pointer        Callee
* x9                        s1                        Saved register                                        Callee
* x10-11                a0-1                Function Arguments/return values Caller
* x12-17                a2-7                Function arguments                                Caller
* x18-27                s2-11                Saved registers                                        Callee
* x28-31                t3-6                Temporaries                                                Caller
*
* The RISC-V context is saved t FreeRTOS tasks in the following stack frame,
* where the global and thread pointers are currently assumed to be constant so
* are not saved:
*
* mstatus
* x31
* x30
* x29
* x28
* x27
* x26
* x25
* x24
* x23
* x22
* x21
* x20
* x19
* x18
* x17
* x16
* x15
* x14
* x13
* x12
* x11
* pvParameters
* x9
* x8
* x7
* x6
* x5
* portTASK_RETURN_ADDRESS
*
* pxCode
*/
本篇完
页: [1]
查看完整版本: [FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers