查看: 1092|回复: 0
收起左侧

漫谈LiteOS-LiteOS SDK支持RISC-V架构

[复制链接]

  离线 

  • TA的每日心情
    奋斗
    2021-3-3 12:32
  • 签到天数: 10 天

    [LV.3]

    发表于 2020-8-24 23:39:28 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 皋陶 于 2020-8-25 18:09 编辑

    【摘要】 本文首先对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
    复制代码

    _rfi:

    1. RESTORE_CONTEXT
    2.         // Return to regular code
    3.         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的初值:

    1.    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打印调通,也是一种较重要的调试手段。


    本篇完,感谢关注:RISC-V单片机中文网




    上一篇:David Patterson RISC-V Foundation 计算机组成与设计:硬件/软件接口
    下一篇:LSM303AGR姿态传感器 risc-v Sifive learn inventor基础之硬件i2c与LSM303
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-3-28 19:14 , Processed in 0.420796 second(s), 45 queries .

    快速回复 返回顶部 返回列表