13918210822 发表于 2024-5-14 04:09:43

蜗牛 发表于 2024-5-13 20:42
不懂RTOS,感觉能实现这样的功能大多数产品也够用了

我也的确希望对于一般的软实时应用够用,
理想状态下,开发者善用纤程设计模式,控制最大纤程体单次进入时间
也可达到特定阀值的硬实时。

13918210822 发表于 2024-5-14 04:28:51

杨为民 发表于 2024-5-13 23:21
等待你的“FOP51”研究结果

目前多任务切换STC8H为20uS的水平,纤程切换应该可以小于1uS


我还在想是否用 宏方式 LJMP with mini contex save 方式替代 LCALL, 从而减少PUSH/POP
毕竟 LCALL/ACALL 虽然只是2周期,但是它压入栈的PC, 要取出来替换还是挺重复的
而LJMP前直接压对应PC到FCB, 虽然多次宏调用增加了代码空间,但是,空间换时间是有效的
毕竟一个单片机代码中,主动阻塞代码是有限的,一个任务中最多也就几个,增加几十~几百个bytes flash还是合算的,宏是不用不占空间。

由于Fiber历来是协作式的,那么FCB 比 TCB要少一些控制
而协作式的多任务,中断也不用记录嵌入层次,中断代码内也不用加对应宏

这么一来一去,应该可以少一些Code空间占用(不能所有库实现都用宏,还是有一个Fibers调度算子适合用LCALL)

至于1us, 的确也是我的目标,
因为切换用到Opcodes多为2周期
24M 1T, 不分频,1us是24周期
如果以20周期为目标,round-robin调度算子,可能是合适的。
如果要实现非抢占优先级,任务优先级再排序估计要额外的几十到几百个周期--所以非必要不要加这个了。

还没有太想清楚,摸着石头走走看,毕竟有些参照。



tzz1983 发表于 2024-5-14 08:37:09

本帖最后由 tzz1983 于 2024-5-14 08:42 编辑

13918210822 发表于 2024-5-14 04:28
我还在想是否用 宏方式 LJMP with mini contex save 方式替代 LCALL, 从而减少PUSH/POP
毕竟 LCALL/ACALL ...
你的想法应该是操作指令直接跳转到另一个函数,
另一个函数处理完自己的事情以后,也可以跳转回来或跳转到其它函数。
这个是可以实现的,如果时钟为24M,1微秒完成保存断点和跳转应该也够了。
但是这个不保存上下文,限制比较大,只在顶级函数之间来跳应该没有问题。
谈不上通用OS,对于一些特定应用还是可以的。祝你成功{:smile:}

补充:
你参照一下TinyRTOS51,他在任务级跳转就是不保存上下文的,但是他保存了调用层次.
如果你只在顶层函数之间跳转,你可以连调用层次也不保存,只需要保存一个断点就可以了

13918210822 发表于 2024-5-14 09:21:09

本帖最后由 13918210822 于 2024-5-14 09:27 编辑

tzz1983 发表于 2024-5-14 08:37
你的想法应该是操作指令直接跳转到另一个函数,
另一个函数处理完自己的事情以后,也可以跳转回来或跳转 ...的确如你所说:

顶层函数,--这个对应 FiberFunc, 类似 ThreadFunc这个层面
也就是说,其他函数应该不要调用阻塞服务函数比如fRead/fSleep之类

多个Fiber,每一个只处理一个事物,比如检测ADC按键,输出LCD1602但不NOP, 比如和上位机通讯等等

Fiber来自于有高并发(本质上还是伪并发)的用台态轻量线程,本质上就是非抢占的

由于少了抢占的需求,有些同步对象可能也就不需要

的确,从这个意义上,不是一个通用“抢占”操作系统服务

我对它定位就是轻(快)量级多任务运行库

至于上下文,如果FiberFunc函数只用static而不直接使用寄存器变量,而处理的IO各不相同。
而其Fiber关联的Fifo 或 DMA Fifo各不相同。那么的确不用保存.

唯一保留的就是 FCB中的 PC_EXT 和 WAIT_LISTS
WAIT_LISTS不属于上下文,因此断点就是唯一要保存的






tzz1983 发表于 2024-5-14 09:34:00

本帖最后由 tzz1983 于 2024-5-14 11:09 编辑

13918210822 发表于 2024-5-14 09:21
的确如你所说:

顶层函数,--这个对应 FiberFunc, 类似 ThreadFunc这个层面

至于上下文,如果FiberFunc函数只用static而不直接使用寄存器变量,而处理的IO各不相同。
而其Fiber关联的Fifo 或 DMA Fifo各不相同。那么的确不用保存.
对于51来说,并非一定要用static, 我让你参照TinyRTOS51 也是想让你明白这个。
你只要有调用函数行为,所有寄存器,编译器都会认为会被新的函数更改,也就是说,
此时所有寄存器都是没有内容的。这个特性你可以利用

13918210822 发表于 2024-5-14 10:15:32

tzz1983 发表于 2024-5-14 09:34
对于51来说,并非一定要用static, 我让你参照TinyRTOS51 也是想让你明白这个。
你只要有调用函数行为, ...

如果不用LCALL 编译器就不会认为是调用,
而即便不用register, keil c51还有overlay, 容易重用临时变量

所以,Fiber强调资源自管理。变量多在在static bytes/global fifo,或者干脆用IO系统Fifo


xiangzichen 发表于 2024-5-16 16:05:09

不小心看到这个东西,看了看感觉挺有意思:

C语言学习-ProtoThread
https://blog.csdn.net/qq_26226375/article/details/128833842

13918210822 发表于 2024-5-19 11:13:35

本帖最后由 13918210822 于 2024-5-19 12:07 编辑

xiangzichen 发表于 2024-5-16 16:05
不小心看到这个东西,看了看感觉挺有意思:

C语言学习-ProtoThread

的确挺特别的,学习了

我设想还是要有一点点汇编的内容,从而可以突破传统的C函数调用的模式
毕竟工具只是工具,要为目标服务

传统上 ISR是不受C语言函数调用约束,而是硬件直接调用中断向量调用(STC8H以尽管支持多极中断)

而其他的函数如果没有多任务环境是中断前硬件保存断点PC到SP指向的位置,而RETI之前POP PC,然后继续原来断点执行。

而有多任务环境的情况下,ISR实际功能完成后,RETI(C return)前,可以调用一个任务切换
宏,替代单纯的RETI

这样可以选择:1.继续支持对应ISR的IST(中断服务用户态任务)2. 做一次任务列表状态更新,以便选择需要断点续行的任务(每个任务的都有自己的断点和状态,是TCB/FCB的重要内容)

而非抢占系统调度,在没有中断发生的情况下,不会主动剥夺任务执行,除非tick中断到来

而任务要主动放弃当前Tick, 可以(类似linux) Sleep(0), 或其他阻塞函数(这个还在梳理细节)

FOP51目的也是一个轻量的多任务环境,类似Fiber的概念.

由于要更多了解Keil C51的一些特性,也在学习比较其他大牛的作品,有些内容还在设计中。。。(不想加太多概念特性)

进度比我希望的慢不少,汗。。。

13918210822 发表于 2024-5-22 10:05:08

本帖最后由 13918210822 于 2024-5-22 10:11 编辑

FOP51理念清单:
0. 8051上的单核多任务(快速轻量)内核。第一版本代号 QR51(使用队列为主要运行同步手段,谐义: 快跑51)
    对应传统意义上硬实时操作系统的部分功能,支持ISR, IST, Fiber (Task) 三层用户程序。
    在没有处理器不同特权级别和MMU等额外运行态情况下,支持多信号源的硬实时应用。
    但其中不包含文件系统,图形界面,抢占式多任务调度,并发锁,多值信号量等一些常见于现代操作系统的必要组件
    临界区也用QR51的内核对象Queue来替换。
    由于没有特权级,QR51是应用程序全可见的运行核心,比较传统操作系统更类似用户态多任务运行库,类比于传统的Fiber.

    QR51在增强型51最小系统(128+ RAM; 1024+ XRAM; 4K+ flash; 4级中断; 可选DMA支持), 支持硬实时,支持60%CPU负载长时运行
   
1. 硬实时,对于定义的事件,均有一个最大响应时间,且不超时。举例如下
   例1:UART2信令第一时间入列,并在1ms解析并做出应答。
   例2:ADC按键在1ms内应答(被记录成按键事件并入列)。
   例3:LCD1602每20ms完成一次刷新
   例4:LED指示灯每500ms完成一次刷新
   。。。
2. ISR 中断服务程序,用户可以自由定义使用,而无须顾忌FOP51的限制
3. IST 中断服务例程,用户通过QR51API定义使用,FOP51会使用优先级调度在每次ISR发生后,调用一次对应IST,
    QR51的IST为短时任务,不能包含无限循环
4. Fiber 用户任务,是一个无限循环的长时任务,可以通过QR51 API qRead主动切换,或者到达时间片被QR51调度切换
    主动切换 qRead (qid, timeout):qid 内核对象id, 0表示单纯sleep timeout;timeout要休眠时间,0表示当前时间片,FF表示尽快返回,其他为具体时间)
    (*51上 LCALL/ACALL包含PC压栈和跳转3周期,LJMP也是3周期,因此放弃之前用包含LJMP的宏调用的设想)
5. 无锁同步,由于目标是轻快,而且Fiber是协作式非抢占调度,因此在同步方式上用队列同步,而不是传统的互斥/信号量/临界区/并发锁等工具。


13918210822 发表于 2024-7-19 16:14:36

本帖最后由 13918210822 于 2024-7-19 16:29 编辑

13918210822 发表于 2024-5-22 10:05
FOP51理念清单:
0. 8051上的单核多任务(快速轻量)内核。第一版本代号 QR51(使用队列为主要运行同步手段 ...
在写这个多任务的平台的过程中, 也加深对多任务在小资源CPU上的理解,
随笔记录, 版本针对8位平台,以后再拓展,至少目前8位平台成熟度性价比更好

Cx51不是标准C, 因为它尽可能少使用栈,从而节约硬件栈的使用(51SP(IDATA <= 256 Bytes))
而多任务的现场决定每个任务运行栈都要是 存续 的, 任务切换出切换进, 任务本身不能有差异,因此,除了ALU和通用寄存器,最重要的是栈要存续。

51历史上20年前陈明计写了SmallRtos, 到最近版友Fanxsp写的TinyRtos, 以及CosyOS和UC/OSII的一些移植版本都让一个 idata空间只有256B的CPU能跑多任务

采用了主栈搬栈的做法,让活动任务占最大剩余空间,这是一个最合理的做法

但是搬栈也有不确定的工作量和时钟消耗,对于任务切换来说,这也是主要的开销之一,任务现场镜像ESI(STACK, Registers)如果不搬栈那么就会快很多
加上主动切换可以等同于函数调用(Cx51编译器保证LCALL之后的代码不会依赖之前的寄存器,所以可以看到SRC中只有interrupt中断代码有寄存器组缓存恢复的指令

后来我发现不止我在动这个脑子,其他版友也多有这个想法,只不过我用的不是单纯的8051, stc8h指令集中直接和间接访问data/idata几乎是一样快的


因此,只要管理好中断嵌入状态和中断代码对压栈的需求,任务栈并不会很大,只需要 2*(调用树+多极中断+NMI)就可以,
为了实现这个,我把中断ISR分成了ISV+ISH+IST三部分,ISV帮助ISH完成了通用的寄存器缓存恢复,IST则公用内核栈。具体过程以后慢慢写。

更重要的是,stc8h上xdata至少也有1k以上,任务内存需求大部分都可以搬到xdata中去
这样,除了0x2F之前的data, 后面除了用户节约下也必用的一点data, 其他都可以用作idata(stacks for tasks) 从 ?STACK开始到0xFF

因此,这就让各任务独立拥有栈成为可以,但是这又限制stc8h上任务数不可能太多,比如32bytes一个任务栈,系统栈64bytes, 一个stc8h最多也就3~5个任务
这也要求编写Q51任务代码的工程师要合并一些业务逻辑到3~5个任务中去

但是,这个限制如果放在edata>2k的32位251上,就不太成为制约。

总之,Q51这个多任务平台,采用不搬栈的方法,任务切换<1us成为了可能。
如果最后是nus, 那么n-1 us基本上是任务仲裁和执行统计的开销,如果采用简单的任务仲裁算法,1~2us的任务切换时间是有保障的

实现上,我发现由于不采用延迟中断切换的模式(ARM流行的PendSV异常状态切换)
任务主动切换的ESI是最小的,也是最快的
抢占切换的开销也没有想象的那么大,大致等同于一次中断处理,但不是在中断中切换。


相反,搞脑子最多的是把ISR分成 ISV+ISH+IST, 其中IST是中断中要处理的逻辑在任务态中执行的部分
虽然麻烦,但是因为缩短了中断时间,从而为硬件中断响应能力提供的保障。也算利大于弊

虽然,很慢很慢才接近于0.1完成,但是从中确实学到很多,原先不明白CPU演变的一些理由,也多了些理解,其实老外也是软硬件协同发展的

给自己加油。。。

参考了版友的不少代码,特别是TinyRTOS, 谢谢各位分享
页: 1 2 3 4 [5] 6
查看完整版本: 最小“非抢占”系统调度服务的必须功能--理念讨论贴,小白发贴,欢迎各位高手及小白