【华为云技术分享】漫谈LiteOS-LiteOS SDK支持RISC-V架构
【摘要】 本文首先对RISC-V的架构做了简要的介绍,在此基础上实现了LiteOS在RISC-V架构上的适配过程的具体步骤,希望对你有所帮助。
1 RISC-V架构简介
RISC-V是一个基于精简指令集(RISC)原则的开源指令集架构(ISA)。
与大多数指令集相比,RISC-V指令集可以自由地用于任何目的,允许任何人设计、制造和销售RISC-V芯片和软件而不必支付给任何公司专利费。RISC-V指令集的设计考虑了小型、快速、低功耗的现实情况来实做,但并没有对特定的微架构做过度的设计。
RISC-V的Spec文档可以在RISC-C官网https://riscv.org/specifications/ 上下载。主要看riscv-privileged.pdf和riscv-spec.pdf。
主要精读的内容包括:
RV32ICM Instruction Set
I:RV32I Base Integer Instruction Set
C:Standard Extension for Compressed Instructions
M:Standard Extension for Integer Multiplication and Division
Privilege Levels
Control and Status Registers (CSRs)
Machine-Level ISA
在了解通用的RV32架构之后,由于RV32是开源的ISA架构,所以实际芯片都会在此基础上做一些定制化,因此需要再读一下芯片手册,LiteOS的RISC-V架构支持使用的芯片是GD32VF103,请下载GD32VF103 的Spec进行阅览。
2 LiteOS支持一种处理器
RTOS支持一种新的处理器架构,最主要的修改有以下几个方面:
1.启动汇编的适配
2.适配系统调度汇编
3.Tick的适配
4.根据芯片设置系统相关参数
5.适配中断管理模块
6.编译链接脚本的调整
那么,对应到LiteOS,主要修改的目录和文件是:
LiteOS_Lab\iot_link\os\liteos\arch\riscv\src中
los_dispatch.S
los_hw.c
los_hw_tick.c
los_hwi.c
和对应芯片target目录下的start.S启动汇编以及ld链接脚本。
步骤如下:
1. start.S
A. 和RISC-V的异常中断处理密切相关,注意向量表的对齐
1 vector_base: 2 j _start 3 .align 2 4 .word 0 5 .word 0 6 .word osInterrupt #eclic_msip_handler 7 .word 0 8 .word 0 9 .word 0 10 .word osInterrupt #eclic_mtip_handler
B. 设置中断,异常等的入口地址
1 _start0800: 2 3 /* Set the the NMI base to share with mtvec by setting CSR_MMISC_CTL */ 4 li t0, 0x200 5 csrs CSR_MMISC_CTL, t0 6 7 /* Intial the mtvt*/ 8 la t0, vector_base 9 csrw CSR_MTVT, t0 10 11 /* Intial the mtvt2 and enable it*/ 12 la t0, irq_entry 13 csrw CSR_MTVT2, t0 14 csrs CSR_MTVT2, 0x1 15 16 /* Intial the CSR MTVEC for the Trap ane NMI base addr*/ 17 la t0, trap_entry 18 csrw CSR_MTVEC, t0
C.设置gp,sp,初始化data和bss section,然后跳转到main函数
1 .option push 2 .option norelax 3 la gp, __global_pointer$ 4 .option pop 5 la sp, _sp 6 7 /* Load data section */ 8 la a0, _data_lma 9 la a1, _data 10 la a2, _edata 11 bgeu a1, a2, 2f 12 1: 13 lw t0, (a0) 14 sw t0, (a1) 15 addi a0, a0, 4 16 addi a1, a1, 4 17 bltu a1, a2, 1b 18 2: 19 /* Clear bss section */ 20 la a0, __bss_start 21 la a1, _end 22 bgeu a0, a1, 2f 23 1: 24 sw zero, (a0) 25 addi a0, a0, 4 26 bltu a0, a1, 1b
2. 适配系统调度汇编(los_dispatch.s),主要修改函数LOS_StartToRun、LOS_IntLock、LOS_IntUnLock、TaskSwitch等;
任务栈的设计,在osTskStackInit中针对RISC-V的寄存器的定义,做出context的设计:
1 pstContext->ra = (UINT32)osTaskExit; 2 pstContext->sp = 0x02020202L; 3 pstContext->gp = 0x03030303L; 4 pstContext->tp = 0x04040404L; 5 pstContext->t0 = 0x05050505L; 6 pstContext->t1 = 0x06060606L; 7 pstContext->t2 = 0x07070707L; 8 pstContext->s0 = 0x08080808L; 9 pstContext->s1 = 0x09090909L; 10 pstContext->a0 = pstTaskCB->uwTaskID; //a0 first argument 11 pstContext->a1 = 0x11111111L; 12 pstContext->a2 = 0x12121212L; 13 pstContext->a3 = 0x13131313L; 14 pstContext->a4 = 0x14141414L; 15 pstContext->a5 = 0x15151515L; 16 pstContext->a6 = 0x16161616L; 17 pstContext->a7 = 0x17171717L; 18 pstContext->s2 = 0x18181818L; 19 pstContext->s3 = 0x19191919L; 20 pstContext->s4 = 0x20202020L; 21 pstContext->s5 = 0x21212121L; 22 pstContext->s6 = 0x22222222L; 23 pstContext->s7 = 0x23232323L; 24 pstContext->s8 = 0x24242424L; 25 pstContext->s9 = 0x25252525L; 26 pstContext->s10 = 0x26262626L; 27 pstContext->s11 = 0x27272727L; 28 pstContext->t3 = 0x28282828L; 29 pstContext->t4 = 0x29292929L; 30 pstContext->t5 = 0x30303030L; 31 pstContext->t6 = 0x31313131L; 32 pstContext->mepc =(UINT32)osTaskEntry;
LOS_IntLock的实现:
1 LOS_IntLock: 2 csrr a0, mstatus 3 li t0, 0x08 4 csrrc zero, mstatus, t0 5 ret
LOS_IntUnLock的实现:
1 LOS_IntUnLock: 2 csrr a0, mstatus 3 li t0, 0x08 4 csrrs zero, mstatus, t0 5 ret
TaskSwitch的实现:
1 TaskSwitch: 2 la t0, g_stLosTask 3 lw t1, 0(t0) 4 csrr t2, mscratch 5 sw t2, 0(t1) 6 7 //Clear the task running bit of pstRunTask. 8 la t0, g_stLosTask 9 lw t1, (t0) 10 lb t2, 0x4(t1) 11 andi t2, t2, OS_TASK_STATUS_NOT_RUNNING 12 sb t2, 0x4(t1) 13 14 //copy pstNewTask into pstRunTask 15 la t0, g_stLosTask 16 lw t1, 0x4(t0) 17 sw t1, 0x0(t0) 18 19 //set the task running bit=1 20 lh t2, 0x4(t1) 21 ori t2, t2, OS_TASK_STATUS_RUNNING 22 sh t2, 0x4(t1) 23 24 //retireve stack pointer 25 lw sp, (t1) 26 27 //retrieve the address at which exception happened 28 lw t0, 31 * 4(sp) 29 csrw mepc, t0 30 31 li t0, 0x1800 32 csrs mstatus, t0 33 34 //retrieve the registers 35 lw ra, 0 * 4(sp) 36 37 lw t0, 4 * 4(sp) 38 lw t1, 5 * 4(sp) 39 lw t2, 6 * 4(sp) 40 lw s0, 7 * 4(sp) 41 lw s1, 8 * 4(sp) 42 lw a0, 9 * 4(sp) 43 lw a1, 10 * 4(sp) 44 lw a2, 11 * 4(sp) 45 lw a3, 12 * 4(sp) 46 lw a4, 13 * 4(sp) 47 lw a5, 14 * 4(sp) 48 lw a6, 15 * 4(sp) 49 lw a7, 16 * 4(sp) 50 lw s2, 17 * 4(sp) 51 lw s3, 18 * 4(sp) 52 lw s4, 19 * 4(sp) 53 lw s5, 20 * 4(sp) 54 lw s6, 21 * 4(sp) 55 lw s7, 22 * 4(sp) 56 lw s8, 23 * 4(sp) 57 lw s9, 24 * 4(sp) 58 lw s10, 25 * 4(sp) 59 lw s11, 26 * 4(sp) 60 lw t3, 27 * 4(sp) 61 lw t4, 28 * 4(sp) 62 lw t5, 29 * 4(sp) 63 lw t6, 30 * 4(sp) 64 65 addi sp, sp, 4 * 32 66 67 mret
3. Tick的适配
osTickStart的启动:
MTIMECMP和MTIME寄存器的设定,TIMER中断的使能,TIMER中断处理函数的注册
1 LITE_OS_SEC_TEXT_INIT UINT32 osTickStart(VOID) 2 { 3 UINT32 uwRet; 4 5 g_uwCyclesPerTick = OS_SYS_CLOCK / LOSCFG_BASE_CORE_TICK_PER_SECOND; 6 g_ullTickCount = 0; 7 8 *(UINT64 *)(TIMER_CTRL_ADDR + TIMER_MTIMECMP) = OS_SYS_CLOCK / LOSCFG_BASE_CORE_TICK_PER_SECOND / 4; 9 10 *(UINT64 *)(TIMER_CTRL_ADDR + TIMER_MTIME) = 0; 11 eclic_irq_enable(CLIC_INT_TMR, 1, 1); 12 LOS_HwiCreate(CLIC_INT_TMR, 3, 0, eclic_mtip_handler, 0); 13 14 g_bSysTickStart = TRUE; 15 16 return LOS_OK; 17 }
4. 根据芯片设置系统相关参数(时钟频率,tick中断配置,los_config.h系统参数配置(内存池大小、信号量、队列、互斥锁,软件定时器数量等));
根据实际开发板的资源和实际使用需求,配置target_config.h的参数和选项。
5. 适配中断管理模块,LiteOS的中断向量表由m_pstHwiForm[OS_VECTOR_CNT]数组管理,需要根据芯片配置中断使能,重定向等;
A.在los_hwi.c和los_hwi.h中根据实际芯片的中断向量数目和驱动做一些调整
B.在entry.S中设计irq_entry的处理,需要注意的是需要单独在irq stack中处理中断嵌套:
1 irq_entry: // -------------> This label will be set to MTVT2 register 2 // Allocate the stack space 3 4 SAVE_CONTEXT// Save 16 regs 5 6 //------This special CSR read operation, which is actually use mcause as operand to directly store it to memory 7 csrrwi x0, CSR_PUSHMCAUSE, 17 8 //------This special CSR read operation, which is actually use mepc as operand to directly store it to memory 9 csrrwi x0, CSR_PUSHMEPC, 18 10 //------This special CSR read operation, which is actually use Msubm as operand to directly store it to memory 11 csrrwi x0, CSR_PUSHMSUBM, 19 12 13 la t0, g_int_cnt 14 lw t1, 0(t0) 15 addi t1, t1, 1 16 sw t1, 0(t0) 17 li t2, 1 18 bgtu t1,t2,service_loop 19 20 csrw mscratch, sp 21 la sp, __irq_stack_top
service_loop:
1 //------This special CSR read/write operation, which is actually Claim the CLIC to find its pending highest 2 // ID, if the ID is not 0, then automatically enable the mstatus.MIE, and jump to its vector-entry-label, and 3 // update the link register 4 csrrw ra, CSR_JALMNXTI, ra 5 6 //RESTORE_CONTEXT_EXCPT_X5 7 8 la t0, g_int_cnt 9 lw t1, 0(t0) 10 addi t1, t1, -1 11 sw t1, 0(t0) 12 bnez t1, _rfi 13 14 csrr sp, mscratch 15 16 DISABLE_MIE # Disable interrupts 17 18 LOAD x5, 19*REGBYTES(sp) 19 csrw CSR_MSUBM, x5 20 LOAD x5, 18*REGBYTES(sp) 21 csrw CSR_MEPC, x5 22 LOAD x5, 17*REGBYTES(sp) 23 csrw CSR_MCAUSE, x5 24 25 la t0, g_usLosTaskLock 26 lw t1, 0(t0) 27 bnez t1, _rfi 28 29 la t0, g_stLosTask 30 lw t1, 0x4(t0) 31 lw t2, 0x0(t0) 32 beq t1, t2, _rfi 33 34 RESTORE_CONTEXT 35 36 push_reg 37 csrr t0, mepc 38 sw t0, 31*4(sp) 39 csrw mscratch, sp 40 j TaskSwitch 41 _rfi: 42 43 RESTORE_CONTEXT 44 // Return to regular code 45 mret
6. 编译链接脚本的调整
几个关键的设置:
irq stack内存区域:
1 __stack_size = DEFINED(__stack_size) ? __stack_size : 2K; 2 __irq_stack_size = DEFINED(__irq_stack_size) ? __irq_stack_size : 2K; 3 __heap_size = DEFINED(__heap_size) ? __heap_size : 0xc00;
gp初始值的设定:gp用于代码的优化,因为请合理选择__global_pointer的初值:
PROVIDE( __global_pointer$ = . + 0x800);
堆栈的设定:
1 .stack : ALIGN(0x10) 2 { 3 . += __stack_size; 4 PROVIDE( _sp = . ); 5 . = ALIGN(0x10); 6 PROVIDE( __irq_stack_bottom = . ); 7 . += __irq_stack_size; 8 PROVIDE( __irq_stack_top = . ); 9 } >ram AT>ram 10 11 .heap : ALIGN(0x10) 12 { 13 PROVIDE( __los_heap_addr_start__ = . ); 14 . = __heap_size; 15 . = __heap_size == 0 ? 0 : ORIGIN(ram) + LENGTH(ram); 16 PROVIDE( __los_heap_addr_end__ = . ); 17 PROVIDE( _heap_end = . ); 18 } >ram AT>ram
主要的步骤已经整体讲述了,顺利移植的主要前提条件是对RISC-V处理器架构的全面理解和LiteOS任务调度的设计,所以再次提醒精读riscv-privileged.pdf和riscv-spec.pdf的相关章节。在移植过程中,会遇到很多问题,建议使用IoT Studio的开发调试环境,方便的进行汇编级的单步调试,另外把串口驱动和printf打印调通,也是一种较重要的调试手段。
作者:星辰27