Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • 社区主页

    FreeRTOS 10.4.3在RISCV(T-HEAD C906)平台上移植过程

    RTOS
    9
    27
    1284
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • C
      caozilong LV 5 last edited by

      好记性不如烂笔头,记录点滴移植经历,一方面便于总结提炼,二是分享,让别人少走有些弯路。

      首先谈一下对FreeRTOS的印象,FreeRTOS还是非常简单的, 要比RT-Thread,Nuttx,SlixOS等偏重型的系统轻量不少,所以FreeRTOS一般不会用在带MMU的重型方案上,而是比较多的应用在如工业控制或者家电等嵌入式场合,在支撑中大规模方案的能力上,FreeRTOS相对比较弱。

      下面开始技术流水账:

      整体的移植框架如下:
      e32cf831-f529-48ea-98bf-df886d7fa088-image.png
      下面说下具体的移植步骤:

      • 搭建构建环境,准备利用melis上已经建好的环境,借”鸡“生”蛋".目标是移植FreeRTOS 系统,所以这部分不重点记录,总之,环境已建好。

      • 用不用sbi? 暂时先用,后续优化掉,因为与Melis相比,FreeRTOS构建的系统相对简单很多,不用支持MMU,整个FreeRTOS系统加上应用都可以运行在M模式,sbi也就失去的存在的必要,但是还是决定先保留着,移植完成后再优化掉。主要考虑三点,1.环境和sbi之间有依赖,包括链接脚本,一些汇编等等,不想这个时候动它,2.SBI本身提供了一些vendor的扩展接口,这些东西暂时还不确定将来会不会用到,如果省去,万一需要还得以某种形式加回来,麻烦。所以暂时先用SBI启动,后续逐渐做减法。3.利用sbi中已经封装好的串口驱动,最小系统启动。用SBI启动,需要都SBI固件做一些小小的修改,改动如下:

      diff --git a/firmware/fw_jump.S b/firmware/fw_jump.S
      index afbcec0..79b3de8 100644
      --- a/firmware/fw_jump.S
      +++ b/firmware/fw_jump.S
      @@ -84,7 +84,7 @@ fw_next_addr:
               * The next address should be returned in 'a0'
               */
       fw_next_mode:
      -       li      a0, PRV_S
      +       li      a0, PRV_M
              ret
      

      修改fw_next_addr 的实现,使用返回的next状态为PRV_M,也就是下阶段是M-Mode,非Linux and Melis运行的S-Mode.,验证如下:0x40010600是OS _star地址,可以正确从SBI跳出,进入这个地址,表示可以进行接下来的CPU引导了.
      a76e0312-8772-4536-91c6-514e9c36a5fb-image.png

      • 下图代表当前处理器功能组件的打开状态,可以看到I/D cache以及PMU相关机制已经开启。
        9e315a15-52a3-434e-bd44-0d189d2c8658-image.png
        mxstatus bit 30 bit 31为 0b11,表示系统运行在machine 模式。
        6812e5a8-68b2-4a24-a9f2-6f41454d63cc-image.png

      • 开发链接脚本,确定最终链接后ELF文件的Layout分布,不启用MMU,所以整体上VA=PA,如下图:
        93247491-efdf-4c5b-ae4c-45da6615e7c3-image.png

      • 关于启动流程的考量

      1. 列表所有ISA定义的寄存器都在确定的状态,这里面包括GPRS和FPU.
      2. 初始化TLB,但M模式不访问页表,这里是为了在S模式中访问内存高地址,因为通过MMU映射后,物理内存可以访问40位,而不仅仅是VPA定义的39位.
      3. 由于页表建立在TLB中,而非内存中,所以这里是直接初始化TLB,TLB中的页表不能被刷出,因为物理内存没有页表项.(这很容易满足,映射和访问范围小于TLB能映射的容量,TLB条目不会被换出,当然,如果存在TLB lock选项,就更有保证了.)
      4. 不同于Linux内核对FPU的访问畏首畏尾,为了让FreeRTOS全速奔跑,这里准备全面支持内核FPU单元加速,采用SR_SD来表示FPU寄存器组的状态实现lazy save/restore.(使用-mabi=lp64d or -mabi=lb64v编译,linux内核只能使用-mabi=lp64编译,用户态才有的选择),
        初始化bringup 堆栈,填满"ebreak"指令,以防不测.
      5. 传递给内核参数包括启动时间,misa以及sbi的位置,SBI后面会优化掉。
        77511c60-cead-468c-939e-35f446832ba6-image.png
      • 工欲善其事,必先利其器,关于打印,进到c之后,最想看到的当然是打印,每个问题都调试汇编效率还是比较低的。这里准备使用newlibc中的printf打印函数,底层对接_write_r到sbi中已经调试好的串口驱动。这中间遇到一个小插曲,在调用printf进行输出的时候,竟然进入了异常 handle_exception里面。 分析发现,原来_write_r是使用的S模式下的SBI封装,调用了ecall指令,这种方式在S模式下当然是可以的,但是问题是,我们准备在M模式下跑FreeRTOS, 所以程序本身就和SBI一样运行于M模式,而M模式的mtvec寄存器已经在上面的流程里面被修改为了新的handle_exception,这里面当然不会处理SBI请求,解决的办法是,将基于"ecall"指令的SBI调用修改为直接对sbi函数的调用。分析过程见下图:
        e13d8381-03b8-4abe-a523-baa498ba7ca7-image.png
        1.强制在handle_exceptions入口停住,打印 mepc
        2.发现mepc指向0x4001b65a,反编译elf文件找到对应位置,发现是"ecall"指令:
        7cf5f6f0-a413-43d1-928b-7a1de0386d30-image.png
      1. 移植SBI中的打印驱动进系统,得到:
        367d020a-2b34-4a99-b49c-fefbf34e566f-image.png
        3ad3f771-5bec-49bb-b821-8ab0196838a3-image.png
      2. 开发arch timer中断功能,使用clic中的compare and value寄存器来作为时钟心跳源,value被24M驱动,当value和compare 相等时,中断被触发,这个时候需要在中断处理中设置下一个触发点的compare值,增量计算方法是24000000/CONFIG_HZ,即每两次中断之间的周期数。首先参考linux做法,在trap_init中开启Machine Mode的Timer中断和External 中断 的capability.
        34ef1bef-45c7-4a55-941c-27ff0ef57749-image.png
        5.开启clic arch timer,注意Mtime和Compare寄存器类似于MIPS中的tick产生机制,这是SIFive的设计,很多vendor Follow.
        7a2a57e2-30a3-408e-8190-895a399c8e6b-image.png
        99ac70ee-1a2a-46d0-97ff-44815775245b-image.png
        6.完整流程为下图,最后一步触发调度器
        fa04deeb-68a8-441d-94f9-0dc702e9bafd-image.png
        7:需要定制的接口包括 xPortStartScheduler, pxPortInitialiseStack, vPortSetupTimerInterrupt, xPortStartFirstTask四个接口,注意arch timer中断的挂接。
        ed5f642e-5521-4d73-a7b8-19e9570fe13f-image.png
        00337144-dbce-4ce3-9596-dfbdbfe8563d-image.png
        0bef51ea-bbcf-44dc-8cf8-401e3a093905-image.png
      3. 任务调度现场的Context布局
        11551a1f-80a8-49a7-bc80-036f92c21694-image.png
        12.实现portasmHANDLE_INTERRUPT, vPortSetupTimerInterrupt定义,用于启用定时器中断和外设中断的处理接口, configCPU_CLOCK_HZ, configTICK_RATE_HZ设置tick中断周期
        13:实现异常和中断处理,将上述的定时器中断接口和外设中断接口挂接到异常处理流程中。
        36bfc181-e448-42e9-980b-c16a9a8da15b-image.png
        14:注意,中断现场的堆栈组织和任务调度现场的堆栈组织是一样的,也和任务初始化的时候堆栈排布一致,由于抢占调度和主动调度都是通过异常路径进入的,这样在freeRTOS里面的处理非常一致,不用区分是中断调度现场还是任务主动出让的调度现场,如下图:
        30ef7d0d-6e92-4b69-bf59-b801c752851e-image.png

      主调度器调度逻辑:
      b0120776-588a-4fc2-9dfa-f1fcc1c099b5-image.png

      15:移植结果:
      aafb6959-b27a-4880-91ea-d698666895d5-image.png

      总结:FreeRTOS作为一个老牌的RTOS,它的可移植性是非常友好的,在移植之前,梳理好几个重要的执行流,比如启动流,主调度器流以及中断流,然后按照FreeRTOS的运行机理将主要的这几个执行流挂接到平台上,基本就实现了整个移植过程。

      关于 ICE, 之前用过不少ICE, 感觉有一个共同点,就是很多高成本一点的ICE仿真器都会内置一个XILINX的FPGA里面,ICE的主要作用是将PC调试指令转换为JTAG时序信号,有很多现成的转换芯片比如FT232,XILINX这里也是起到协议转换的作用,但是FPGA的逻辑断电侯会消失,ICE是如何保证调试逻辑不消失的呢?经过了解,ICE里面一般内置了一个Flash或者EEPROM,用于存放固化的逻辑,类似于FPGA物理验证阶段使用的bitfile,每次上电FPGA都会从EEPROM里面load bitfile设计逻辑。不过有点扯淡的是,虽然CKLink Pro用的XINLINX FPGA作主控,但CKLink Lite却出奇的省,仅仅用了STM32做主控。从价格也可以看出两者的明显不同,前者淘宝1200, 后者只有200,主控一换,1000块出去了。

      Jlink 不能用,原因还是因为THEAD用了自家的调试IP,但是应该有一种可行的破解方法,就是抓出cklink协议然后修改OpenOCD,使在同样的GDB命令下,OpenOCD驱动JLink产生同样的输出,因该是个可行的办法.

      Marleo M 3 Replies Last reply Reply Quote Share 0
      • tigger
        tigger LV 7 last edited by

        跟紧大佬的脚步,晚点我也试一试。

        1 Reply Last reply Reply Quote Share 0
        • Marleo
          Marleo LV 4 @caozilong last edited by

          This post is deleted!
          1 Reply Last reply Reply Quote Share 0
          • Marleo
            Marleo LV 4 @caozilong last edited by

            @caozilong 你好,在FreeRTOSConfig.h中,需要配置configMTIME_BASE_ADDRESS和configMTIMECMP_BASE_ADDRESS,但是C906没有mtime寄存器啊,该怎么设置呢?

            C 1 Reply Last reply Reply Quote Share 0
            • C
              caozilong LV 5 @Marleo last edited by

              @marleo 在 FreeRTOS 10.4.3在RISCV(T-HEAD C906)平台上移植过程 中说:

              mtime
              你好,mtime是m-mode arch register,是属于RISCV 架构SPEC定义的内容,C906一定是有的。
              只不过,mtime寄存器在实现的时候,vendor可以选择两种方式去实现,第一种是访问寄存器的方式隐藏在指令中,不暴露寄存器出来,比如通过rdtime,rdcycle指令等等,第二种是所谓的MMIO方式,将mtime映射为一篇存储区,直接给地址访问。
              据我了解,C906是第二种方式实现的,第一种指令也支持,只不过支持方式是陷入SBI模拟去模拟实现的,本质上也是直接读取的MMIO内存。
              这篇移植版本由于整个FREERTOS系统运行在M模式下,有权限直接读取MMIO地址获取timer,上面的流程图也有画这部分。
              想要看细节,要看SBI的代码了,可以grep 关键字 rdtime,rdcycle或者ilegal systemcall等等,就会找到线索。

              T Marleo 2 Replies Last reply Reply Quote Share 0
              • T
                TheOne LV 2 @caozilong last edited by

                @caozilong 小白刚学习移植FreeRTOS,具体应该怎么得知Mtime基地址呢,可以方便直接说一下吗.

                D 1 Reply Last reply Reply Quote Share 0
                • D
                  dreamer LV 4 @TheOne last edited by

                  @theone
                  C906手册里面有喔

                  Marleo 1 Reply Last reply Reply Quote Share 0
                  • Marleo
                    Marleo LV 4 @dreamer last edited by

                    @dreamer ad926436-c425-4f51-b83f-65c156118d1f-image.png 只有mtime比较寄存器地址,没有mtime寄存器啊

                    C 1 Reply Last reply Reply Quote Share 0
                    • C
                      caozilong LV 5 @Marleo last edited by

                      @marleo 偏移BFFF8位置处就是,你看的这份文档应该是没有更新,可以和源码交叉对照一起看。

                      Marleo 1 Reply Last reply Reply Quote Share 0
                      • M
                        molin2050 LV 5 @caozilong last edited by

                        @caozilong 大哥你这水平,月薪多少啊

                        1 Reply Last reply Reply Quote Share 0
                        • Marleo
                          Marleo LV 4 @caozilong last edited by

                          @caozilong e54ec7eb-c6fd-48c8-a372-f677982f5ad0-image.png 我看的是这个版本的,去平头哥官网看了下,目前还是这个版本的,请问您能分享下更新后的版本吗?

                          1 Reply Last reply Reply Quote Share 0
                          • C
                            caozilong LV 5 last edited by caozilong

                            原因是这样的:
                            首先说结论,MTIME 一定是有的,只是vendor在设计的时候,微架构上的实现不同。
                            业内在RISCV架构的中断部分实现中有两个标准,一个叫CLIC,另一个是PLIC,CLIC访问MTIME的方式就是前面说的,通过 ”clinc+0xbff8"寻址mtime,也就是”基地址+偏移“的形式,这种形式比较普遍。
                            而C906不是完全按照CLIC的标准定义这一块的,它使用了CLIC+PLIC,并没有把MTIME的地址给映射出来,而是通过rdtime 伪指令,这条指令编译的时候会扩展为csr指令读取 C906的扩展寄存器,而这个扩展寄存器就是和mtime绑定的。
                            所以C906上的做法可能不是完全符合CLIC的标准设计,如果你使用TH的E906,可能就是另外一个样子,E906上,中断控制器是完全按照CLIC设计的,可以用读地址的方式读取MTIME的值。
                            这里不会对你的有太多妨碍的,时钟配对了,上电MTME就会起来。

                            1 Reply Last reply Reply Quote Share 0
                            • Marleo
                              Marleo LV 4 @caozilong last edited by

                              @caozilong 您好,对于第一种方式,我通过rdtime指令是可以读取mtime寄存器的值,对于第二种MMIO方式,直接去访问其地址,在freertos的vPortSetupTimerInterrupt()函数中,有对mtime和cmp进行操作,这里我设置的地址是mtime=0x1400BFF8,cmp=0x14004000,但是当芯片上电运行后,会陷入“Load access fault”异常,通过读取mtval寄存器里的值,发现其等于0x1400BFF8,所以,mtime这里该怎么设置呢?还有您的移植过程的第五点,CSR_PLIC_BASE指的是PLIC的基地址吧?谢谢。

                              C 1 Reply Last reply Reply Quote Share 0
                              • C
                                caozilong LV 5 @Marleo last edited by

                                @marleo MMIO方式是行不通的,根据向Vendor的了解,C906并没有将Mtime映射出来,你这样直接访问这个地址肯定是会出异常的。
                                C906只支持1种方式,rdtime获取时钟数.

                                M 1 Reply Last reply Reply Quote Share 0
                                • M
                                  March LV 5 @caozilong last edited by

                                  @caozilong 那vPortSetupTimerInterrupt()函数是需要改写吧?

                                  C 1 Reply Last reply Reply Quote Share 0
                                  • C
                                    caozilong LV 5 @March last edited by

                                    @march 是的,要定制,它属于架构相关的结构,所以才叫"vPortxxxxx"

                                    M 1 Reply Last reply Reply Quote Share 0
                                    • M
                                      March LV 5 @caozilong last edited by

                                      @caozilong xPortStartScheduler, pxPortInitialiseStack, vPortSetupTimerInterrupt, xPortStartFirstTask,这四个函数都需要定制,是吧?还有其他的嘛?

                                      C 1 Reply Last reply Reply Quote Share 0
                                      • C
                                        caozilong LV 5 @March last edited by caozilong

                                        @march 有些忘记了,图中画的是当初遇到的, 同样的事情,你遇到的情况和我遇到的情况可能是不同的。建议看完后把它忘掉,自己淌一遍,把每个问题当成新问题去思考解决,博客只是参考,不要依赖。

                                        Marleo 1 Reply Last reply Reply Quote Share 0
                                        • Marleo
                                          Marleo LV 4 @caozilong last edited by

                                          @caozilong 您好,我只修改了vPortSetupTimerInterrupt函数,创建了三个简单的线程,刚开始时,线程能正常调度运行,但是运行177s后,线程便卡死,且不管创建几个线程,都是177s后卡死,请问可能的原因是什么呢?另外能分享下需要定制的函数源码吗?

                                          C 1 Reply Last reply Reply Quote Share 0
                                          • C
                                            caozilong LV 5 @Marleo last edited by

                                            @marleo 在 FreeRTOS 10.4.3在RISCV(T-HEAD C906)平台上移植过程 中说:

                                            听起来像是某个变量固定在某个时间溢出了,和计时器相关的逻辑要注意下。而且你的调度器都已经正常工作了,适配的大方向应该都是对的了,可能细节方面要留意一些。

                                            后面可能需要对mtval, mcause,以及RISCV的异常处理流程多多了解一下,类似的问题绝对不会只有这一条,裸机开发最困扰人的就是类似的死机问题,需要对处理器架构相关的细节多多了解。
                                            刚刚找了一下,由于没有在产品中使用freeRTOS(用的rt-thread), 之前移植的成果找不到了。我的代码没有,你可以放码上来,我们可以针对具体的问题进行讨论。

                                            M 1 Reply Last reply Reply Quote Share 0
                                            • M
                                              March LV 5 @caozilong last edited by whycan

                                              @caozilong 您好,具体的移植过程以及源码: D1_FreeRTOS.rar

                                              https://whycan.com/t_7299.html#p69778
                                              里面用了很多printf打桩调试,请忽略。谢谢。

                                              C 1 Reply Last reply Reply Quote Share 0
                                              • C
                                                caozilong LV 5 @March last edited by

                                                @march 在 FreeRTOS 10.4.3在RISCV(T-HEAD C906)平台上移植过程 中说:

                                                过程

                                                这里面细节比较多,有些忘记了,我找找当初移植的代码吧,到时候你参考一下。

                                                Marleo 1 Reply Last reply Reply Quote Share 0
                                                • Marleo
                                                  Marleo LV 4 @caozilong last edited by

                                                  @caozilong 请问有找到了嘛🙏 非常感谢

                                                  C 1 Reply Last reply Reply Quote Share 0
                                                  • C
                                                    caozilong LV 5 @Marleo last edited by caozilong

                                                    @marleo ft.tar.bz2
                                                    仅供参考。

                                                    Marleo L 2 Replies Last reply Reply Quote Share 0
                                                    • Marleo
                                                      Marleo LV 4 @caozilong last edited by

                                                      @caozilong 您好,参考了您的代码,在system_tick_init()函数中调用了get_cycles_hi()和get_cycles()函数来获取mtime的值,我在移植到哪吒开发板上,会出现”illegal instruction“异常,于是分别改写了system_tick_init和riscv_timer_interrupt函数,其余和arch以及freertos配置相关的基本和您的代码保持一致,但是现象还是运行一定时间就会卡死,求大佬hlep。D1_FreeRTOS_20211105_4.rar

                                                      void system_tick_init(void)
                                                      {
                                                          csr_clear(mie, MIE_MTIE | MIE_MSIE);
                                                          write32(CLINT + 0x4000, counter() + delta);
                                                          write32(CLINT + 0x4004, 0);
                                                          csr_set(mie,  MIE_MTIE); 
                                                      }
                                                      
                                                      void riscv_timer_interrupt(void)
                                                      {
                                                          csr_clear(mie, MIE_MTIE);
                                                          *(uint64_t*)CLINT_MTIMECMPL(0) = counter() + delta;
                                                          csr_set(mie, MIE_MTIE);
                                                      }
                                                      
                                                      static inline uint64_t counter(void)
                                                      {
                                                      	uint64_t cnt;
                                                      	 __asm__ __volatile__("csrr %0, time\n" : "=r"(cnt) :: "memory");
                                                      	return cnt;
                                                      }
                                                      S 1 Reply Last reply Reply Quote Share 0
                                                      • Referenced by  S smiletiger 
                                                      • S
                                                        smiletiger LV 5 @Marleo last edited by

                                                        @marleo 请问解决死机问题了吗

                                                        1 Reply Last reply Reply Quote Share 0
                                                        • L
                                                          leanrd_chen00918 LV 2 @caozilong last edited by

                                                          @caozilong 请问您的这份代码如何编译呢?

                                                          1 Reply Last reply Reply Quote Share 0
                                                          • 1 / 1
                                                          • First post
                                                            Last post

                                                          Copyright © 2021 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

                                                          行为准则 | 用户协议 | 隐私权政策