On supporting RISC-V architecture with liteos liteos SDK

[Abstract] this paper first gives a brief introduction of RISC-V architecture, and then implements the specific steps of LiteOS adaptation process on RISC-V architecture, hoping to help you.

1 Introduction to risc-v architecture


RISC-V is an open source instruction set architecture (ISA) based on the principle of reduced instruction set (RISC).

Compared with most instruction sets, risc-v instruction sets can be freely used for any purpose, allowing anyone to design, manufacture and sell risc-v chips and software without paying any company patent fee. The design of RISC-V instruction set considers the reality of small, fast and low power consumption, but does not over design the specific micro architecture.

Spec documents of RISC-V can be found on RISC-C official website https://riscv.org/specifications/ Up and down. Mainly see riscv-privileged.pdf and riscv-spec.pdf.

The main intensive reading contents include:

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

After understanding the general RV32 architecture, because RV32 is an open source ISA architecture, the actual chips will be customized on this basis, so it is necessary to read the chip manual again. The chips supported by LiteOS RISC-V architecture are GD32VF103. Please download the Spec of GD32VF103 for reading.

2 LiteOS supports a processor

RTOS supports a new processor architecture. The main modifications are as follows:

1. Start assembly adaptation

2. Scheduling compilation of adaptive system

3.Tick adaptation

4. Set system related parameters according to chip

5. Adaptive interrupt management module

6. Adjustment of compile link script

Then, corresponding to LiteOS, the main modified directories and files are:

In liteos lab \ IOT link \ OS \ liteos \ arch \ riscv \ SRC










And start.S start assembly and ld link script under the corresponding chip target directory.

The steps are as follows:

1. start.S

A. It is closely related to RISC-V's abnormal interrupt handling. Pay attention to the alignment of the vector table

        j _start
        .align     2
        .word     0
        .word     0
        .word     osInterrupt #eclic_msip_handler
        .word     0
        .word     0
        .word    0
        .word    osInterrupt #eclic_mtip_handler

B. Set the entry address of interrupt, exception, etc

        /* Set the the NMI base to share with mtvec by setting CSR_MMISC_CTL */
        li t0, 0x200
        csrs CSR_MMISC_CTL, t0
        /* Intial the mtvt*/
        la t0, vector_base
        csrw CSR_MTVT, t0
        /* Intial the mtvt2 and enable it*/
        la t0, irq_entry
        csrw CSR_MTVT2, t0
        csrs CSR_MTVT2, 0x1
        /* Intial the CSR MTVEC for the Trap ane NMI base addr*/
        la t0, trap_entry
        csrw CSR_MTVEC, t0

C. Set gp, sp, initialize data and bss section, then jump to main function

    .option push
        .option norelax
        la gp, __global_pointer$
        .option pop
        la sp, _sp
        /* Load data section */
        la a0, _data_lma
        la a1, _data
        la a2, _edata
        bgeu a1, a2, 2f
        lw t0, (a0)
        sw t0, (a1)
        addi a0, a0, 4
        addi a1, a1, 4
        bltu a1, a2, 1b
        /* Clear bss section */
        la a0, __bss_start
        la a1, _end
        bgeu a0, a1, 2f
        sw zero, (a0)
        addi a0, a0, 4
        bltu a0, a1, 1b

2. To adapt to the system scheduling assembly (Los? Dispatch. S), mainly modify the functions Los? Starttorun, Los? INTLOCK, Los? Intunlock, TaskSwitch, etc;

For the design of task stack, the context is designed in ostskstackenit according to the definition of RISC-V register:

    pstContext->ra = (UINT32)osTaskExit;
    pstContext->sp = 0x02020202L;
    pstContext->gp = 0x03030303L;
    pstContext->tp = 0x04040404L;
    pstContext->t0 = 0x05050505L;
    pstContext->t1 = 0x06060606L;
    pstContext->t2 = 0x07070707L;
    pstContext->s0 = 0x08080808L;
    pstContext->s1 = 0x09090909L;
    pstContext->a0 = pstTaskCB->uwTaskID;         //a0 first argument
    pstContext->a1 = 0x11111111L;
    pstContext->a2 = 0x12121212L;
    pstContext->a3 = 0x13131313L;
    pstContext->a4 = 0x14141414L;
    pstContext->a5 = 0x15151515L;
    pstContext->a6 = 0x16161616L;
    pstContext->a7 = 0x17171717L;
    pstContext->s2 = 0x18181818L;
    pstContext->s3 = 0x19191919L;
    pstContext->s4 = 0x20202020L;
    pstContext->s5 = 0x21212121L;
    pstContext->s6 = 0x22222222L;
    pstContext->s7 = 0x23232323L;
    pstContext->s8 = 0x24242424L;
    pstContext->s9 = 0x25252525L;
    pstContext->s10 = 0x26262626L;
    pstContext->s11 = 0x27272727L;
    pstContext->t3 = 0x28282828L;
    pstContext->t4 = 0x29292929L;
    pstContext->t5 = 0x30303030L;
    pstContext->t6 = 0x31313131L;
    pstContext->mepc =(UINT32)osTaskEntry;

Implementation of Los? INTLOCK:

        csrr    a0, mstatus
        li      t0, 0x08
        csrrc   zero, mstatus, t0

The implementation of Los? Intunlock:

        csrr    a0, mstatus
        li      t0, 0x08
        csrrs   zero, mstatus, t0

Implementation of TaskSwitch:

        la      t0, g_stLosTask
        lw      t1, 0(t0)
        csrr    t2, mscratch
        sw      t2, 0(t1)
        //Clear the task running bit of pstRunTask.
        la      t0, g_stLosTask
        lw      t1, (t0)
        lb      t2, 0x4(t1)
        andi    t2, t2, OS_TASK_STATUS_NOT_RUNNING
        sb      t2, 0x4(t1)
        //copy pstNewTask into pstRunTask
        la      t0, g_stLosTask
        lw      t1, 0x4(t0)
        sw      t1, 0x0(t0)
        //set the task running bit=1
        lh      t2, 0x4(t1)
        ori     t2, t2, OS_TASK_STATUS_RUNNING
        sh      t2, 0x4(t1)
        //retireve stack pointer
        lw      sp, (t1)
        //retrieve the address at which exception happened
        lw      t0, 31 * 4(sp)
        csrw    mepc, t0
        li     t0, 0x1800
        csrs   mstatus, t0
        //retrieve the registers
        lw      ra, 0 * 4(sp)
        lw      t0, 4 * 4(sp)
        lw      t1, 5 * 4(sp)
        lw      t2, 6 * 4(sp)
        lw      s0, 7 * 4(sp)
        lw      s1, 8 * 4(sp)
        lw      a0, 9 * 4(sp)
        lw      a1, 10 * 4(sp)
        lw      a2, 11 * 4(sp)
        lw      a3, 12 * 4(sp)
        lw      a4, 13 * 4(sp)
        lw      a5, 14 * 4(sp)
        lw      a6, 15 * 4(sp)
        lw      a7, 16 * 4(sp)
        lw      s2, 17 * 4(sp)
        lw      s3, 18 * 4(sp)
        lw      s4, 19 * 4(sp)
        lw      s5, 20 * 4(sp)
        lw      s6, 21 * 4(sp)
        lw      s7, 22 * 4(sp)
        lw      s8, 23 * 4(sp)
        lw      s9, 24 * 4(sp)
        lw      s10, 25 * 4(sp)
        lw      s11, 26 * 4(sp)
        lw      t3, 27 * 4(sp)
        lw      t4, 28 * 4(sp)
        lw      t5, 29 * 4(sp)
        lw      t6, 30 * 4(sp)
        addi    sp, sp, 4 * 32

3. Tick adaptation

Start of osTickStart:

Setting of MTIMECMP and MTIME registers, enabling of TIMER interrupt, registration of TIMER interrupt processing function

        UINT32 uwRet;
        g_ullTickCount = 0;
        *(UINT64 *)(TIMER_CTRL_ADDR + TIMER_MTIME) = 0;
        eclic_irq_enable(CLIC_INT_TMR, 1, 1);
        LOS_HwiCreate(CLIC_INT_TMR, 3, 0, eclic_mtip_handler, 0);
        g_bSysTickStart = TRUE;
        return LOS_OK;

4. Set the system parameters (clock frequency, tick interrupt configuration, Los config. H system parameter configuration (memory pool size, semaphore, queue, mutex, number of software timers, etc.) according to the chip;

Configure the parameters and options of target config. H according to the actual development board resources and actual use requirements.

5. Adaptive interrupt management module, the interrupt vector table of LiteOS is managed by M ﹣ psthwiform [OS ﹣ vector ﹣ CNT] array, which needs to be configured with interrupt enable and redirect according to the chip;

A. In Los? HWI. C and Los? HWI. H, some adjustments are made according to the number of interrupt vectors and the driver of the actual chip

B. When designing IRQ "entry processing in entry.S, it should be noted that interrupt nesting needs to be handled separately in irq stack:

 irq_entry: // -------------> This label will be set to MTVT2 register
        // Allocate the stack space
        SAVE_CONTEXT// Save 16 regs
        //------This special CSR read operation, which is actually use mcause as operand to directly store it to memory
        csrrwi  x0, CSR_PUSHMCAUSE, 17
        //------This special CSR read operation, which is actually use mepc as operand to directly store it to memory
        csrrwi  x0, CSR_PUSHMEPC, 18
        //------This special CSR read operation, which is actually use Msubm as operand to directly store it to memory
        csrrwi  x0, CSR_PUSHMSUBM, 19
        la t0, g_int_cnt
        lw t1, 0(t0)
        addi t1, t1, 1
        sw t1, 0(t0)
        li t2, 1
        bgtu t1,t2,service_loop
        csrw mscratch, sp
        la sp, __irq_stack_top


//------This special CSR read/write operation, which is actually Claim the CLIC to find its pending highest
        // ID, if the ID is not 0, then automatically enable the mstatus.MIE, and jump to its vector-entry-label, and
        // update the link register 
        csrrw ra, CSR_JALMNXTI, ra 
        la t0, g_int_cnt
        lw t1, 0(t0)
        addi t1, t1, -1
        sw t1, 0(t0)
        bnez t1, _rfi
        csrr sp, mscratch
        DISABLE_MIE # Disable interrupts
        LOAD x5,  19*REGBYTES(sp)
        csrw CSR_MSUBM, x5
        LOAD x5,  18*REGBYTES(sp)
        csrw CSR_MEPC, x5
        LOAD x5,  17*REGBYTES(sp)
        csrw CSR_MCAUSE, x5
        la t0, g_usLosTaskLock
        lw t1, 0(t0)
        bnez t1, _rfi
        la      t0, g_stLosTask
        lw      t1, 0x4(t0)
        lw      t2, 0x0(t0)
        beq  t1, t2, _rfi
        csrr t0, mepc
        sw t0, 31*4(sp)
        csrw mscratch, sp
        j TaskSwitch


        // Return to regular code

6. Adjustment of compile link script

Several key settings:

irq stack memory area:

 __stack_size = DEFINED(__stack_size) ? __stack_size : 2K;
        __irq_stack_size = DEFINED(__irq_stack_size) ? __irq_stack_size : 2K;
        __heap_size = DEFINED(__heap_size) ? __heap_size : 0xc00;

Setting of initial value of gp: gp is used for code optimization, because please select the initial value of global pointer reasonably:

   PROVIDE( __global_pointer$ = . + 0x800);

Stack settings:

  .stack : ALIGN(0x10)
        . += __stack_size;  
        PROVIDE( _sp = . ); 
        . = ALIGN(0x10);
        PROVIDE( __irq_stack_bottom = . );
        . += __irq_stack_size;
        PROVIDE( __irq_stack_top = . );
    } >ram AT>ram 
    .heap : ALIGN(0x10)
        PROVIDE( __los_heap_addr_start__ = . );
        . = __heap_size;
        . = __heap_size == 0 ? 0 : ORIGIN(ram) + LENGTH(ram);
        PROVIDE( __los_heap_addr_end__ = . );
        PROVIDE( _heap_end = . );
    } >ram AT>ram

The main steps have been described as a whole. The main prerequisite for a smooth migration is a comprehensive understanding of RISC-V processor architecture and the design of LiteOS task scheduling. Therefore, I would like to remind you to read the relevant chapters of riscv-privileged.pdf and riscv-spec.pdf. In the process of migration, many problems will be encountered. It is suggested to use the development and debugging environment of IoT Studio to facilitate the assembly level single step debugging. In addition, the serial port driver and printf print debugging are also important debugging means.

Author: Star 27

991 original articles published, 5399 praised, 820000 visitors+
His message board follow

Posted on Wed, 05 Feb 2020 05:53:50 -0500 by mtorbin