[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers

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

[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第1张图片

[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第2张图片第二个搜了一下是NXP的RV32M1,用了4个core,2个risc-v的,2个ARM的,RISC-V的core是RI5CY和zero RI5CY.

ETH Zurich 是瑞士的苏黎世联邦理工, 而Università di Bologna是意大利的博洛尼亚大学[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第3张图片

[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第4张图片  [FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第5张图片   [FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第6张图片

第三个就是更具有代表性的risc-v官方公司sifive了,具体的信息并不清楚。

 

该文档主要阐述了针对risc-v的6处修改:

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

1/2,增加一个针对risc-v CPU的定义宏文件( freertos_risc_v_chip_specific_extensions.h ) ,实际当你使用某risc-v core时一定会有一个类似的定义文件:

#define portasmHAS_CLINT 1 

a Core Local Interrupter (CLINT)   ,表示CPU是否有内部timer interrupt可用于tick;

#define portasmADDITIONAL_CONTEXT_SIZE 0

CPU有几个额外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;[FreeRTOS] Using FreeRTOS on RISC-V Microcontrollers_第7张图片

以及其他针对该CPU的设定,例如:scr1的IPIC register地址和置位宏就可以定义在此

#define IPIC_CISV					0xBF0
#define IPIC_CICSR					0xBF1
#define IPIC_IPR					0xBF2
#define IPIC_ISVR					0xBF3
#define IPIC_EOI                    0xBF4
#define IPIC_SOI                    0xBF5
#define IPIC_IDX                    0xBF6
#define IPIC_ICSR                   0xBF7
#define IPIC_ICSR_IP                (1 << 0)
#define IPIC_ICSR_IE                (1 << 1)
#define IPIC_ICSR_IM                (1 << 2)
#define IPIC_ICSR_INV               (1 << 3)
#define IPIC_ICSR_IS                (1 << 4)

3,freertosconfig.h增加相关宏:

如果CLINT=1,Timer地址定义在此,scr1的如下

#define configCLINT_BASE_ADDRESS		0x490000

4,修改了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

官宣里定义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
 * [chip specific registers go here]
 * pxCode
 */

 

你可能感兴趣的:(SCR1,risc-v,FreeRTOS)