【华为云技术分享】漫谈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

 

posted @ 2020-02-29 16:46  华为云官方博客  阅读(875)  评论(0编辑  收藏  举报