AtomThreads RTOS移植到低RAM(4K)的芯片

目录

一些吐槽

航顺和APT

移植经验

任务初始化函数

​​​​​​​上下文切换函数

​​​​​​​OS第一个任务运行函数

​​​​​​​系统中断函数

​​​​​​​最后的个人建议

性能测试

这才是本文核心


一些吐槽

2021年对中国的很多电子产品公司是一个很特殊的年份,全产业链的芯片奇缺,导致生产成本暴涨。在以前普通控制芯片价格不高于1元的情况下,现在5倍的价格都不一定能拿到,而且供货非常不稳定。

谁能想到,这些低端芯片会这么缺货!市场需求因为疫情突然暴增是一个诱因之一,但是主要是IOT的产业爆炸扩大了需求,而芯片厂家产能又不能及时快速提高。

对于很对公司来说,找到可以替换正在使用的缺货芯片成了生存之本。

平常使用STM32/8及类似的芯片,因为供货稳定和可以接受的价格,软件都已经成熟了。尤其是配套的RTOS及网上各种技术资料漫天飞,是嵌入式工程师的最爱。

遇到今年的状态,导致该芯片需要替换,如何最大限度的降低成本和最快的移植程序到芯片上成了攻城狮们的目标。

笔者在行业里摸鱼了几年,玩过5毛一片的国产SOP16的芯片,也玩过Cortex-A/R系列芯片,FreeRTOS和UcOS,安卓,Linux都去体验了一把。今年的芯片缺货让我想到了这是低端芯片尤其是国产的芯片突围的最佳时机。以下有广告嫌疑,建议相关厂商给个广告费哦。

因为这次缺货,我才知道国产的低端芯片已经做的很好,Cortex-M系列中有航顺,几乎可以替换STM32Fxx的芯片,价格还比STM32低很多,号称不高于1元,不过因为疫情也涨价了,也比STM32便宜,但也很难拿到货。除此外,基于阿里平头哥的APT的芯片在功能上可以满足很多厂商的需求。

航顺的芯片对标的是STM32,在芯片配置上比APT强很多,两者面向的客户群体不一样。APT的芯片面向的是以前用STM8及更低端芯片的客户,STM8及类似芯片在嵌入式上多以裸机或者简单的轮询系统运行,很少有运行RTOS,STM32基本都会使用RTOS,当然土豪不差钱的公司可以用大炮打蚊子。

航顺和APT

终于说到了正事上来了。

基于以上选择,我两个芯片都买来试了一下,体验真的不一样。

航顺芯片做的是真比APT好太多,以前在STM32/8运行的程序,几乎可以无缝移植,连库函数的名字都差不多。

APT芯片在使用上真的让人难受,首先是产品手册写的烂,还有使用demo程序让人不能恭维。真的是人比人,气死人。再看库函数,我的内心要崩溃了,还不如我自己直接在代码里封装函数控制寄存器舒服。一个函数需要传递的参数可以超过10个。我都怀疑该公司技术团队是不是半路出家的。APT的芯片基于平头哥的自主内核架构设计,采用RISC命令集,CPU的各个方面功能都还不错,尤其是ROM是真良心,有64K之多,可是RAM就小气了,只有4K,这么低的RAM,是很难移植常用的RTOS的。而且咨询了官方,官方的回复是,不支持操作系统,都是跑裸机。这感觉是我弱我有理啊。

笔者用习惯了操作系统,突然写裸机程序,真是要了命了,虽然折腾了半个月把裸机的代码敲出来了,但是自己看着这个程序就是一坨屎,恶心的很。想不通为啥64K的flash还不能用操作系统,网上找了相关资料,低RAM的芯片大多都是裸机,其中主要原因是功能实时性要求不高,也没有太多需求,很多高端的嵌入式工程师看不起这么低端芯片,几乎没人在这里投入精力。

在网上找到了Atomthreads的相关资料,这个是一个极小的RTOS,网上能找到的资料几乎都是2016年之前的,非常少,还都是国内的兄弟们移植到STM8上的验证资料。去官网查看了,这个RTOS本身支持STM8,源代码都是现成的。

没办法,只能去Atomthreads的官网查看了,读完了官网资料,下载了源代码,花了一周业余时间,读完了所有源码,包括支持STM8和cortex-M、AVR等的移植源码,发现这个系统移植到APT上是可行的。而航顺采用的是Cortex-M架构,天然支持该芯片。此时,基于航顺的所有芯片都可以使用操作系统了。航顺的大容量芯片可以使用Freertos,小RAM芯片可以使用Atomthreads,此处不多说航顺芯片了。对于APT芯片的移植开始了。

寻找资料,APT的芯片是基于平头哥的CK801内核设计,所以找APT的官方人员要相关资料,唉,回复没有。我也不奢求他们能给出啥有用的回复。自己去平头哥官网找吧,官方资料竟然只有英文版,好吧,我也忍了。下载了一堆资料,最后比较有用的就是《C-SKY+ABIV2+Standards+Manual.pdf》和《T-HEAD_800_Series_ABI_Standards_Manual.pdf》、《CSKY Architecture user_guide.pdf》、《C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf》。文档下载链接https://github.com/c-sky/csky-doc

其实就是看下这个架构下各个通用寄存器的功能还有几个指令,这里可以给大家推荐一个视频,视频有讲RSIC-V汇编指令的内容,https://www.bilibili.com/video/BV1o7411E7ka。如果想自己完成移植的朋友,建议看下这个视频,有帮助的。

移植经验

以下是个人移植过程中的经验。

英文不错的朋友建议读一下官网移植指南:http://atomthreads.com/index.php?q=node/4

先说这个RTOS的核心,基本就是任务切换和现场保护。每个任务有独立的栈,任务切换的时候,将需要保存的寄存器压栈,然后将任务指针切换到其他任务。

按照官网给出的建议,需要实现4个函数就可以完成移植:

​​​​​​​任务初始化函数

archThreadContextInit()

void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(UINT32), UINT32 entry_param)

void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param)
{
    uint32_t *stack_ptr;

    /** Start at stack top */
    tcb_ptr->sp_save_ptr = stack_ptr = stack_top;


    *stack_ptr-- = (uint32_t)thread_shell;

    /**
     * Store starting register values for R4-R8
     */

    
    *stack_ptr-- = (uint32_t) 0x00000808;   /* R8 */
    *stack_ptr-- = (uint32_t) 0x00000707;   /* R7 */
    *stack_ptr-- = (uint32_t) 0x00000606;   /* R6 */
    *stack_ptr-- = (uint32_t) 0x00000505;   /* R5 */
    *stack_ptr   = (uint32_t) 0x00000404;   /* R4 */

    /**
     * All thread context has now been initialised. Save the current
     * stack pointer to the thread's TCB so it knows where to start
     * looking when the thread is started.
     */
    tcb_ptr->sp_save_ptr = stack_ptr;
}

这个函数主要是完成task创建的初始化,尤其是指针的运用尤其重要,这个函数移植不难,参考其他架构的移植样例,几乎可以照搬。使用C语言编程即可。

任务初始化的时候,需要模拟该任务已经有保存了上下文,所以有模拟压栈操作。因为在任务启动的时候,内核会调用上下文切换函数,将栈内的值出栈,其实任务在刚运行的时候,栈内的值都是无效的。但是在移植的时候,还是建议设置一些值,这些值在任务刚启动会出栈到对应寄存器中。

​​​​​​​上下文切换函数

archContextSwitch()

archContextSwitch(ATOM_TCB *old_tcb, ATOM_TCB *new_tcb)

archContextSwitch:
    push      	r4-r8, r15             /* Save registers */

    st.w        r14, (r0)              /* Save old SP in old_tcb_ptr->sp_save_ptr (first TCB element) */
    ld.w        r14, (r1)              /* Load new SP from new_tcb_ptr->sp_save_ptr (first TCB element) */

    pop			r4-r8,r15             /* Load new registers */

简单的说,就是用来保存当前任务的上下文和寄存器里的值到栈里,俗称压栈。官网建议使用汇编指令编写。

这里官网给出了建议:

  • 线程因进入暂停状态而自行切换(延时/信号量/队列)
  • 来自中断处理程序,因为新线程已准备好运行(其他任务抢占式切换)

这两个状态都会触发任务切换,这个时候需要保存任务上下文。也就是当前的寄存器值和SP栈指针。查看了C-SKY的手册,R0~R3是函数的参数寄存器和返回值寄存器,R4~R11是函数的本地寄存器,R12~R13是临时寄存器,R14是栈指针(SP),R15是lr寄存器。也就是说,官方编译器在编译C程序的时候,会自动保存R0~R3,其他的寄存器是我们需要压栈的。所以只需要保存R4~R13即可。

这里APT来作妖了,该芯片只有16个32位通用寄存器,通过IDE在线debug发现APT没有R9~R12寄存器,在压栈的时候,也只需要保存R4~R8、R15就可以了。

SP的值是来自函数的参数。第一个参数是当前task的栈指针,需要保存该值。第二个参数是新task的栈指针,需要加载到SP中。

这个函数就是旧任务压栈,新任务出栈操作。

​​​​​​​OS第一个任务运行函数

archFirstThreadRestore()

archFirstThreadRestore(ATOM_TCB *new_tcb)

_archFirstThreadRestore:

	ld.w        r14, (r0)              /* Get SP (sp_save_ptr is conveniently first element of TCB) */
    pop			r4-r8, r15             /* Load new registers */

这个函数就是在OS启动的时候,第一个task启动的时候使用,后续的都不需要使用了。

按照官方的推荐,这个函数就是完成archContextSwitch()的后半段操作,新任务出栈操作即可。

​​​​​​​系统中断函数

最后是OS的系统中断,为系统提供一个滴答定时器,这个在每个RTOS中都有,就不详细说了。

APT的芯片我采用了CORET定时器作为OS的定时器。相关资料请参考APT的产品手册(真的写的烂)

​​​​​​​最后的个人建议

最后,有个容易忽略的地方,每个系统都有一个临界段,在Atomthreads官网中没有提到这个问题,但是看源代码,每个架构的临界段命令都不一样,所以这也是在移植过程中需要注意的地方。CK8xx系列全局中断的指令是在PSR寄存器里ie位设置的,相关资料参考《CSKY Architecture user_guide.pdf》中3.2.2节关于PSR寄存器的介绍(全英文),所以移植的时候,需要修改临界段进出的宏定义。这个在我移植好的代码中atomport.h有体现

#define CRITICAL_STORE          //uint32_t __atom_critical

#define CRITICAL_START()        asm  ("psrclr ie");

#define CRITICAL_END()          asm  ("psrset ie");

CRITICAL_STORE不能删除,内核有使用该函数。

性能测试

完成了移植,需要来测试一下性能了。

下图是使用APT官方给的最简单工程编译后的截图。该工程就只是初始化了时钟和看门狗,main函数就一直while中喂狗。编译后代码大概为4K。RAM的使用主要还是APT的库函数使用的。个人感觉APT的库函数写的是真烂,如果对RAM使用要求很高的,可以考虑删减不需要的库函数或者不用库函数,自己封装库函数更好。

下图是移植了Atomthreads后,并创建了2个任务使用了信号量/队列/定时器后的代码量,代码大概为9K,其中有1k是2个任务的代码,实际RTOS使用了约4k的ROM。这个在64K的ROM中是可以接受的。

内存增大的原因是我给每个任务分配了512字节的内存,同时idle任务的内存也是201个字节。也就是说bss段中有1225字节都是预分给任务使用的,RTOS实际使用了约100字节。APT有4k的内存,足够分配很多任务了。而且任务需要的内存可以再降低,我尝试使用128字节为任务的内存,在比较复杂的任务逻辑也可以运行,这个需要在编程过程中考虑内存分配,这里不再详述。

以下是串口截图,可以看到任务之间的切换是正常的。

相关demo工程和移植好的代码已经上传到github上,请移步下载https://github.com/tracediary/ck8xx_atomthreads

这才是本文核心

我估计会有人说,低端芯片实际应用使用裸机跑程序已经可以了,没必要用操作系统,代码和逻辑会变的更复杂。这句话说的我无言以对。使用操作系统后,代码和逻辑的确会变的复杂,而且非常考验攻城狮的水平。但是因为低端芯片就不用操作系统这思维是不对的,这对个人的发展非常不好,一个没有全局思维的编程者就是流水线的钉子,做的所有东西都是重复劳动。使用操作系统的好处是将问题分解并快速处理,这样在后续如果需要添加新功能,可以避免和减少对原有功能的影响。这个是裸机编程很难避免的。

总之,不要待在自己的舒适区,否则被淘汰是迟早的。

本人微信公众号:TraceDiary

不过更新慢,哈哈

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值