为 PicoRV32 适配 rt-thread 操作系统

RISC-V 上丁 ⋅ 于 2020-04-11 22:00:55 ⋅ 281 阅读

为PicoRV32适配rt-thread操作系统

PicoRV32是一款由著名的IC设计师Clifford Wolf开发并开源的一款RISC-V处理器核,它实现RISC-V RV32IMC指令集,这款PicoRV32的重点在于面积和频率的优化,在作者的github上我们了解到在Xilinx7-Series FPGA上的开销为761-2000 LUTs,并且能够综合到250~450MHz的主频。而我们这次使用的安陆EAGLE_20系列FPGA LUTs资源为19600,足以放入PicoRV32核心。

rt-thread是一款国产实时操作系统,采用c语言编写,具有体积小,成本低,功耗低、启动快速,实时性高、占用资源小等特点。其NANO版本仅占用3kb的flash资源,1.2kb的RAM资源,非常适合PicoRV32。

​目前我们已经成了rt-threadPicoRV32移植的初步工作,使得rt-thread能够在PicoRV32核心上正常运行,并实现基本的上下文切换操作。

01

如下是我们使用VSCODE和RISCV-gcc工具链编译rtthread

yang@DESKTOP-3E9KST5 MINGW64 ~/Desktop/work_space/firmware-flash_2
$ make
riscv32-unknown-elf-gcc -I"C:\Users\yang\Desktop\work_space\firmware-flash_2" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\include" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\libcpu" -I"C:\Users\yang\Desktop\work_space\firmware-flash_2\rt-thread\finsh"  -ffreestanding -nostdlib -o firmware.elf start.S firmware.c  ./rt-thread/src/*.c  ./rt-thread/libcpu/*.c  ./rt-thread/finsh/*.c   ./rt-thread/libcpu/*.S  -march=RV32I --std=gnu99 -Wl,-Bstatic,-T,sections.lds,-Map,firmware.map,--strip-debug -lgcc
riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin

yang@DESKTOP-3E9KST5 MINGW64 ~/Desktop/work_space/firmware-flash_2
$

02

移植的第一步首先是得完成在PicoRV32裸机状态下的运行程序,确保cpu能正常运作和启动代码的有效。然后把入口地址(main)指向操作系统的入口地址(entry)。

#include "custom_ops.S"

.section .text

start:
    # zero initialize entire scratchpad memory
    li a0, 0x00000000
    li a1, 0x00001000
setmemloop:
    sw a0, 0(a0)
    addi a0, a0, 4
    blt a0, a1, setmemloop

    # copy data section
    la a0, _sidata
    la a1, _sdata
    la a2, _edata
    bge a1, a2, end_init_data
loop_init_data:
    lw a3, 0(a0)
    sw a3, 0(a1)
    addi a0, a0, 4
    addi a1, a1, 4
    blt a1, a2, loop_init_data
end_init_data:

# zero-init bss section
    la a0, _sbss
    la a1, _ebss
    bge a0, a1, end_init_bss
loop_init_bss:
    sw zero, 0(a0)
    addi a0, a0, 4
    blt a0, a1, loop_init_bss
end_init_bss:

    # zero-initialize register file
    addi x1, zero, 0
    # x2 (sp) is initialized by reset
    addi x3, zero, 0
    addi x4, zero, 0
    addi x5, zero, 0
    addi x6, zero, 0
    addi x7, zero, 0
    addi x8, zero, 0
    addi x9, zero, 0
    addi x10, zero, 0
    addi x11, zero, 0
    addi x12, zero, 0
    addi x13, zero, 0
    addi x14, zero, 0
    addi x15, zero, 0
    addi x16, zero, 0
    addi x17, zero, 0
    addi x18, zero, 0
    addi x19, zero, 0
    addi x20, zero, 0
    addi x21, zero, 0
    addi x22, zero, 0
    addi x23, zero, 0
    addi x24, zero, 0
    addi x25, zero, 0
    addi x26, zero, 0
    addi x27, zero, 0
    addi x28, zero, 0
    addi x29, zero, 0
    addi x30, zero, 0
    addi x31, zero, 0

    lui sp, %hi(0x400);
    addi sp, sp, %lo(0x400);
    # call main
    call entry
loop:
    j loop

.balign 4

03

编写上下文切换汇编函数、这里是系统启动执行的第一个任务,关键是要需要将任务的入口函数地址和返回地址复制到通用寄存器中

   .globl rt_hw_context_switch_to
    rt_hw_context_switch_to:
    lw sp, (a0)
/*

    lw a0,   0 * 4(sp)
    csrw mepc, a0                  load epc from stack 机器模式异常pc寄存器,现在把thread1的入口存入
*/

    lw x1,   1 * 4(sp)               /*ra  返回地址*/

/*
    lw a0,   2 * 4(sp)   load mstatus from stack
    csrw mstatus, a0     机器模式状态寄存器,7880意味着mpp为11强制为机器模式MPIE0,MIE为1,
*/
    lw x4,   4 * 4(sp)
    lw x5,   5 * 4(sp)
    lw x6,   0 * 4(sp)   /*把入口保存在临时寄存器t1*/
    lw x7,   7 * 4(sp)
    lw x8,   8 * 4(sp)
    lw x9,   9 * 4(sp)
    lw x10, 10 * 4(sp)
    lw x11, 11 * 4(sp)
    lw x12, 12 * 4(sp)
    lw x13, 13 * 4(sp)
    lw x14, 14 * 4(sp)
    lw x15, 15 * 4(sp)
    lw x16, 16 * 4(sp)
    lw x17, 17 * 4(sp)
    lw x18, 18 * 4(sp)
    lw x19, 19 * 4(sp)
    lw x20, 20 * 4(sp)
    lw x21, 21 * 4(sp)
    lw x22, 22 * 4(sp)
    lw x23, 23 * 4(sp)
    lw x24, 24 * 4(sp)
    lw x25, 25 * 4(sp)
    lw x26, 26 * 4(sp)
    lw x27, 27 * 4(sp)
    lw x28, 28 * 4(sp)
    lw x29, 29 * 4(sp)
    lw x30, 30 * 4(sp)
    lw x31, 31 * 4(sp)

    addi sp,  sp, 32 * 4
    jr t1

04

编写任务切换汇编代码

  .globl rt_hw_context_switch
rt_hw_context_switch:

    /* saved from thread context
     *     x1/ra       -> sp(0)
     *     x1/ra       -> sp(1)
     *     mstatus.mie -> sp(2)
     *     x(i)        -> sp(i-4)
     */
    addi  sp,  sp, -32 * 4
    sw sp,  (a0)

    sw x1,   0 * 4(sp)    
    sw x1,   1 * 4(sp)

/*    csrr a0, mstatus         */
    andi a0, a0, 8
    beqz a0, save_mpie
    li   a0, 0x80
save_mpie:
    sw a0,   2 * 4(sp)

    sw x4,   4 * 4(sp)
    sw x5,   5 * 4(sp)
    sw x6,   6 * 4(sp)
    sw x7,   7 * 4(sp)
    sw x8,   8 * 4(sp)
    sw x9,   9 * 4(sp)
    sw x10, 10 * 4(sp)
    sw x11, 11 * 4(sp)
    sw x12, 12 * 4(sp)
    sw x13, 13 * 4(sp)
    sw x14, 14 * 4(sp)
    sw x15, 15 * 4(sp)
    sw x16, 16 * 4(sp)
    sw x17, 17 * 4(sp)
    sw x18, 18 * 4(sp)
    sw x19, 19 * 4(sp)
    sw x20, 20 * 4(sp)
    sw x21, 21 * 4(sp)
    sw x22, 22 * 4(sp)
    sw x23, 23 * 4(sp)
    sw x24, 24 * 4(sp)
    sw x25, 25 * 4(sp)
    sw x26, 26 * 4(sp)
    sw x27, 27 * 4(sp)
    sw x28, 28 * 4(sp)
    sw x29, 29 * 4(sp)
    sw x30, 30 * 4(sp)
    sw x31, 31 * 4(sp)

    /* restore to thread context
     * sp(0) -> epc;
     * sp(1) -> ra;
     * sp(i) -> x(i+2)
     */
    lw sp,  (a1)      /*参数二*/

    /* resw ra to mepc */
    lw a1,   0 * 4(sp)
/*    csrw mepc, a1      有*/
    lw x1,   1 * 4(sp)

    /* force to machin mode(MPP=11) */
/*    li a1, 0x00001800; @*/
/*    csrs mstatus, a1   @*/
/*    lw a1,   2 * 4(sp)  @*/
/*    csrs mstatus, a1    @*/

    lw x4,   4 * 4(sp)
    lw x5,   5 * 4(sp)
    lw x6,   0 * 4(sp)
    lw x7,   7 * 4(sp)
    lw x8,   8 * 4(sp)
    lw x9,   9 * 4(sp)
    lw x10, 10 * 4(sp)
    lw x11, 11 * 4(sp)
    lw x12, 12 * 4(sp)
    lw x13, 13 * 4(sp)
    lw x14, 14 * 4(sp)
    lw x15, 15 * 4(sp)
    lw x16, 16 * 4(sp)
    lw x17, 17 * 4(sp)
    lw x18, 18 * 4(sp)
    lw x19, 19 * 4(sp)
    lw x20, 20 * 4(sp)
    lw x21, 21 * 4(sp)
    lw x22, 22 * 4(sp)
    lw x23, 23 * 4(sp)
    lw x24, 24 * 4(sp)
    lw x25, 25 * 4(sp)
    lw x26, 26 * 4(sp)
    lw x27, 27 * 4(sp)
    lw x28, 28 * 4(sp)
    lw x29, 29 * 4(sp)
    lw x30, 30 * 4(sp)
    lw x31, 31 * 4(sp)

    addi sp,  sp, 32 * 4
    jr t1

05

创建两个测试任务,由于没有启用内存堆,采用静态创建的方式创建任务。

void main()
{
   rt_kprintf("now at main\n\r");

   rt_err_t result;
        /* 创建线程1 */
   tid1 = &thread_1;
   rt_kprintf("tid1 = &thread1 done;\n\r");
   result = rt_thread_init(tid1, "thread_1", thread1_entry, RT_NULL,
                            thread1_stack, sizeof(thread1_stack), 5, 20);  //优先级,时间片
   if(result == RT_EOK){
      rt_kprintf("thread1 startup...    ");
      rt_thread_startup(tid1);
      rt_kprintf("done!\r\n\r\n");
    }
    else{
         print("init thread1 fail\n\r");
    }
        /* 创建线程2 */

  tid2 = &thread_2;
  rt_kprintf("\r\n\r\nnow init thread2...");
  result = rt_thread_init(tid2, "thread2", thread2_entry, RT_NULL,
                            thread2_stack, sizeof(thread2_stack), 6, 20);  //优先级,时间片
  if(result == RT_EOK){
        print("thread2 startup\n\r");
      rt_thread_startup(tid2);
    }
    else{
          print("init thread2 fail\n\r");
    }
  rt_kprintf("\r\n\r\n\r\nall thing is ok!\r\n");
  rt_kprintf("let go to scheduler now!\r\n");

    rt_system_scheduler_start();
}

06

编写任务代码,测试任务切换功能。第一个任务运行一段时间后挂起自身,让出cpu的使用权。使得任务2得以执行。

 void thread1_entry(void *param)
{
    rt_uint32_t t=0;
    rt_kprintf("now at thread1\r\n");
    while (1)        //慢
    {

        leds = 0x80;
      for(uint32_t i=0;i<10000;i++)asm volatile("");
      leds = 0x40;
      for(uint32_t i=0;i<10000;i++)asm volatile("");
      rt_kprintf("t = %d\r\n",t);
      t++;
      if (3==t)
        {
          rt_kprintf("rt_thread_suspend....");

          if(rt_thread_suspend(tid1) == RT_EOK)            //rt_thread_suspend(tid1);
          {
                rt_kprintf("ok!");
                rt_schedule();
              t=0;
          }

          }
    }
}

 void thread2_entry(void *param)
{   
    char a=0;
    rt_kprintf("now at thread2\r\n");

    while (1)           
    {
      leds = 0x80;
      for(uint32_t i=0;i<2000;i++)asm volatile("");
      leds = 0x40;
      for(uint32_t i=0;i<2000;i++)asm volatile("");
      a++;
      rt_kprintf("a = %d\r\n",a);
      if(15==a)
      {
          rt_thread_resume(tid1);
          rt_schedule();
          a=0;

      }
    }
}

07

第一个任务启动前要在调度函数里打印一下栈帧的内容情况确保内容有效。

08

运行的结果:任务在来回切换,串口打印不同的内容。led在两个任务的闪烁频率不同

ok!highest_ready_priority is : 6
 local is : 00000780   value :a0520 
 local is : 00000784   value :a406c 
 local is : 00000788   value :3888 
 local is : 0000078c   value :deadbeef 
 local is : 00000790   value :deadbeef 
 local is : 00000794   value :deadbeef 
 local is : 00000798   value :deadbeef 
 local is : 0000079c   value :deadbeef 
 local is : 000007a0   value :deadbeef 
 local is : 000007a4   value :deadbeef 
 local is : 000007a8   value :0 
 local is : 000007ac   value :deadbeef 
 local is : 000007b0   value :deadbeef 
 local is : 000007b4   value :deadbeef 
 local is : 000007b8   value :deadbeef 
 local is : 000007bc   value :deadbeef 
 local is : 000007c0   value :deadbeef 
 local is : 000007c4   value :deadbeef 
 local is : 000007c8   value :deadbeef 
 local is : 000007cc   value :deadbeef 
 local is : 000007d0   value :deadbeef 
 local is : 000007d4   value :deadbeef 
 local is : 000007d8   value :deadbeef 
 local is : 000007dc   value :deadbeef 
 local is : 000007e0   value :deadbeef 
 local is : 000007e4   value :deadbeef 
 local is : 000007e8   value :deadbeef 
 local is : 000007ec   value :deadbeef 
 local is : 000007f0   value :deadbeef 
 local is : 000007f4   value :deadbeef 
 local is : 000007f8   value :deadbeef 
 local is : 000007fc   value :deadbeef 
 rt_hw_context_switch...now at thread2
a = 1
a = 2
a = 3
a = 4
a = 5
a = 6
a = 7
a = 8
a = 9
a = 10
a = 11
a = 12
a = 13
a = 14
a = 15
jump over rt_hw_interrupt_disable();

jump rt_hw_interrupt_enable(temp);

into rt_schedule_insert_thread... jump interrupt disable

insert thread to ready list ...done!jump interrupt enable

done!

highest_ready_priority is : 5
local is : 00000530   value :a3c5c 
 local is : 00000534   value :a3c5c 
 local is : 00000538   value :80 
 local is : 0000053c   value :a04 
 local is : 00000540   value :deadbeef 
 local is : 00000544   value :a261c 
 local is : 00000548   value :a0438 
 local is : 0000054c   value :deadbeef 
 local is : 00000550   value :5d0 
 local is : 00000554   value :6 
 local is : 00000558   value :80 
 local is : 0000055c   value :ac4 
 local is : 00000560   value :a6280 
 local is : 00000564   value :594 
 local is : 00000568   value :b3c 
 local is : 0000056c   value :ac4 
 local is : 00000570   value :0 
 local is : 00000574   value :deadbeef 
 local is : 00000578   value :deadbeef 
 local is : 0000057c   value :deadbeef 
 local is : 00000580   value :deadbeef 
 local is : 00000584   value :deadbeef 
 local is : 00000588   value :deadbeef 
 local is : 0000058c   value :deadbeef 
 local is : 00000590   value :deadbeef 
 local is : 00000594   value :deadbeef 
 local is : 00000598   value :deadbeef 
 local is : 0000059c   value :deadbeef 
 local is : 000005a0   value :deadbeef 
 local is : 000005a4   value :deadbeef 
 local is : 000005a8   value :deadbeef 
 local is : 000005ac   value :deadbeef 
 rt_hw_context_switch...done!
t = 0
t = 1
本帖已被设为精华帖!
本帖由 YX 于 2月前 加精
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter