找回密码
 立即注册
楼主: 13918210822

最小“非抢占”系统调度服务的必须功能--理念讨论贴,小白发贴,欢迎各位高手及小白

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-7-24 06:37:38 | 显示全部楼层
本帖最后由 139182*** 于 2024-7-24 06:42 编辑
13918210822 发表于 2024-7-19 16:14
在写这个多任务的平台的过程中, 也加深对多任务在小资源CPU上的理解,
随笔记录, 版本针对8位平台,以后再 ...

随笔-- 51不搬栈的多任务实现方式

由于51硬件栈(push, pop, ret, reti, scall, acall, lcall)这些使用硬件栈(sp/idata)的指令
所能使用的内存最多256字节,而前8个字节是寄存器组0不能使用,因此,keil中如果没有
额外初始化,启动后sp为07H, 如果使用了初始化 sp = #?stack-1(排在所有其他data/idata定义之后), 实际上最多也就100~200字节左右

因此,传统上51的RTOS均采用搬栈这种方式,就是切换任务的时候把其他栈占用的内容搬移到sp~0FFH以外的区域,而如果使用了small reentrant,还要减去 ?C_IBP~0FFH这部分
所以,51上用small reentrant不太“安全”,硬件栈顶(向上生长)和模拟栈底(向下生长)搞不好就碰撞,两个栈一碰系统99.9999%要崩溃。

即便51 reentrant,只是用Large模式,硬件栈和模拟栈是不会碰了,
但如果按照任务栈各自独立的“运行效果”,对于一个有4级中断优先级+NMI的51(stc8h)
那么不加干预,任务栈的使用大约如下
函数xCALL每次占用2个字节保存RET返回地址
中断入口时占用2个字节保存RETI返回地址
但是Keil C51 interrupt关键字会生成保存寄存器组的指令代码,这个会占用 5~14字节(保存寄存器数量会根据interrupt函数代码优化),那么考虑4级非NMI+1级NMI
那么,允许8级调用树+4级非NMI+NMI,栈要保留 16+15*5 = 91字节
这样,任务栈在256字节内,最多只能同时存在2个。如此,搬栈似乎必不可少?

但如果类似PSP模式切SP不搬栈,则任务切换速度会大大提升,多任务效率会随之受益。

因此,就想到把 中断栈独立出来,这样任务栈只要保留8xCALL*2+4就可以 ~20字节
这样128 高idata可以支持6个任务的 PSP

那么VSP(Vector Stack 中断栈) 需要 15*4+NMI(手工汇编优化不占用栈, 因为NMI不会被嵌套) = 60字节

这样如果不使用using, 不使用interrupt关键字,还有 256-60-120-8=68(52 data + 16bdata)
这样配合xdata存储dma和其他buffer,是可以支持不搬栈多任务的。

备忘。。。

点评

不错,思维非常缜密  详情 回复 发表于 2024-7-24 08:03
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:5
  • 最近打卡:2025-04-30 07:46:22

33

主题

1079

回帖

2319

积分

荣誉版主

积分
2319
发表于 2024-7-24 08:03:53 | 显示全部楼层
本帖最后由 tzz1983 于 2024-7-24 09:11 编辑
139182*** 发表于 2024-7-24 06:37
随笔-- 51不搬栈的多任务实现方式

由于51硬件栈(push, pop, ret, reti, scall, acall, lcall)这些使用硬 ...

不错,思维非常缜密 , 除PendISR外, 其它中断不会切换任务,可以统一使用一个中断专用栈
一个合理的配制,PendISR + TICK滴答最低级, 其它中断只开放两级(Priority_1 + Priority_2),如此一来,即便中断内有一些调用,60字节中断专用栈也足够。
任务栈计划每个任务30字节,即最大的调用深度是(30-15)/2 = 7 级(有点少), 一般应用也够。 假设4个任务  30*4=120字节。

OS总的栈用量 = (60+120),  还有一点剩余,留给APP吧 。  

这大致上就是不移栈的51核运行OS的场景,可以想象, 切换任务的速度将和STC32G 在同一个档次, 起飞吧。

总体来说,优点很明显,切换任务加快了很多很多.   缺点也很明显,调用深度受限, 任务数量受限, 中断级也受限, 但也还算过得去.
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-7-24 18:40:32 | 显示全部楼层
tzz1*** 发表于 2024-7-24 08:03
不错,思维非常缜密 , 除PendISR外, 其它中断不会切换任务,可以统一使用一个中断专用栈
一个合理的配制 ...

谢谢指导
切任务,目前没有采用PendSV, 而是Q方式,在NMI的IST(任务态)中具体处理
好处是NMI_ISR保持一个高速(~500ns@44M), IST部分慢一点也是任务态

目前tick设定在100us级别,因此,超时或优先级抢占不会延迟太久(在时间片到达才作判断)
也参考了你的NoPendSV版本的uC/OSII,不过这部分策略还在优化中,慢慢汇报

于此相对的是主动切换,如你所说主动切换上下文只有断点和栈(3字节),因此是直接任务态内直接切换的(<1us)

吐嘈,C51的编译器不开源,还有一些小bug, 只好用纯汇编凑一些功能,。。。


慢慢收敛了,加油中...
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-7-25 03:49:22 | 显示全部楼层
tzz1*** 发表于 2024-7-24 08:03
不错,思维非常缜密 , 除PendISR外, 其它中断不会切换任务,可以统一使用一个中断专用栈
一个合理的配制 ...

20字节的任务栈不包含抢占时寄存器缓存(那个存入TCB)

中断发生时,先切VSP, 因此20字节是 最大8层调用(2*8) + 第一次中断返回地址2 + NMI返回地址2(NMI可能在第一次中断还来不及切栈时发生,硬件把返回地址入任务栈)

8层调用,考虑库函数最多占用2~3层,用户最好只用5层,如果不够,则加大任务栈



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-7-26 08:23:07 | 显示全部楼层
本帖最后由 13918210822 于 2024-7-26 11:52 编辑

随笔--Q51任务与IST
任务属于传统RTOS
但是51栈空间很小,不搬栈任务数不可能太多

因此,有一些跟随ISR的后续处理通过IST完成,IST是短任务, IST每次均从函数入口进,完成后返回内核,因此不需要保留独立栈

ISV+ISH+IST 三级结构构成Q51的驱动程序

在信号处理大部分由IST处理完成后, Fiber任务即便只有4~6个也是够用的

虽然最初立意是“非抢占”,开发到现在发现其实抢占对于资源的消耗也不是很多,可以把FCB(就是TCB)放在xdata即可
而抢占对于Q51的架构来说,其实没有什么改变。只要有中断发生触发任务状态改变,其实也可以在相应驱动完成后,立刻切换

估计会配制成2种,支持抢占和非抢占,预计非抢占更有优势,抢占的部分可被IST承担。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-8-6 14:36:09 | 显示全部楼层
本帖最后由 13918210822 于 2024-8-6 14:37 编辑

随笔--技巧RET用作动态参数跳转-替代函数指针与函数指针数组, 函数指针原型 void (*funcptr)(void), 如果要加参数代码要相应修改
_CALL_FUNC_ARRAY:                 ;R7传递函数指针数组的下标
    MOV  A, R7
    RL A                                   ;A保存了函数指针数组的字节偏移
    ADD A, #LOW(func_array)
    PUSH ACC
    CLR A
    ADDC A, #HIGH(func_arry)
    PUSH ACC
    RET

PUBLIC _CALL_FUNC_ARRAY

c语言申明   extern void CALL_FUNC_ARRAY(data u8 idx) small;

CALL_FUNC_ARRAY(0); //调用函数指针数组的第一个函数

参考备忘。。。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-8-7 06:01:50 | 显示全部楼层
本帖最后由 13918210822 于 2024-8-12 12:09 编辑

关于Timer0 Mode3 NMI中断的设计思路


Timer0 Mode3 是 51/251中唯一的NMI中断, 也是最高优先级的陷入方式, 这意味着不受EA的限制, 这非常有用, 比如用户程序处于某种目的关闭了总中断
但是NMI仍然可以推动操作系统或驱动乃至用户应用的某些功能按照定时去执行

考虑到这一点, Fiber51内核把ISR_NMI作为系统Tick中断来使用

但是Fiber51追求中断效率和任务切换速度, 顺带着把系统Tick频率也提高到10K以上.
如此Tick中断要求进出非常高效, 也不要执行多余的操作比如切换栈, 除了硬件LCALL压入的返回点甚至不使用栈.

因此,把必须在中断中完成的内容放在ISR_NMI中进行, 主要进行SysTick Count和Fiber's Delay and Timeout Update

如此可以把进出ISR_NMI的时间控制在1~2us, 在此时间内更新时钟和超时特性, 设置对应Fiber51 Running Status Word (FRSW虚拟寄存器)
这样在返回任务前的中断历程(ISR_NMI_EXT或INTR_EXT)中, 可以判断FRSW从而选择返回任务或者转到内核服务例程IST0. IST0会执行后续操作并给其他IST和Fiber以运行权
如果ISR_NMI是非嵌套中断, 那么根据需要转移到内核则在ISR_NMI退出前完成
如果ISR_NMI发生时是嵌套中断, 那么根据需要转移到内核就由INTR_EXT完成, INTR_EXT是除ISR_NMI外每一个中断入口向量ISV的第三条指令 LJMP INTR_EXT
ISV的三条指令(占用8个字节)为
ACALL INTR_ENT
LCALL ISH_NAME       ;ISH_NAME与VECTOR_NAME相对应, 比如DMA_UR1R_VECTOR对应的ISH_NAME是ISH_DMA_UR1R
LJMP  INTR_EXT


而NMI中断的入口是 Timer0 ISV入口, 只有一条指令
LJMP ISR_NMI

ISR_NMI具体实现只用汇编实现, 大多数情况小于1us执行时间




回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2024-8-14 23:37:56 | 显示全部楼层
随笔--任务状态与任务状态链表
多任务同时执行数量受限于执行单元的数量, 在目前stc8h/32g上只有1个,
          Q51定位于stc8h, 因此事实上特定时刻只有一个任务可以被执行, 内核可以看成一个特殊的任务--守护服务任务
         

          中断的处理不在此主题赘述
         

          作为一个任务至少有三种状态, 在执行状态, 未执行状态; 而未执行状态又至少分为2种,
                1.就绪:可以执行但暂时没有被执行

                2.等待:暂时还不具备执行条件, 需要满足特性条件集合才能具备执行可能性
          加上1个在运行任务, 那么所有任务按照状态就有 3个子集合. 3个子集合的元素是不断变化
          在一个硬实时操作系统中, 任务的先后排队基于所谓的优先级调度算法有确定的先后顺序
          那么, 除了最原始的遍历结构, 顺序表中的双向链表是一个合适的数据结构



          Q51中维护一个 QTID(在运行任务ID), 和 fl_wait, fl_ready 2个任务状态双向链表 -- 参考了fanxsp的大作tinyrtos51 也用了类似任务表
         不过Q51中只有Fiber才是任务, 而IST是不可切换只能中断的(没有独立栈,在内核上下文执行), 所以最多<10个任务(主要是任务有独立栈)
         因此,用4位表示一个ID,  因此链表节点为1个字节, 高4位为prev, 低4位为next
         

         2个任务状态表均为 TASKS + 1字节, 头节点字节如果 为0, 则任务状态内节点为空
         任务ID 从 1到10(默认配置为4), 任务ID 0被指代内核任务.

typedef struct _flist
{
        u8 fpre:        4;                //列表中前一个Fiber的ID,Q51最多支持15个fiber, 因此4位足够
        u8 fnext:        4;                //列表中后一个Fiber的ID,因为Fiber0是内核不参与排队,因此0代表没有后续
} flist;
         因此, 只要从头指针遍历就可以决定, 为加速操作, 加入就绪表采用找到比待加入更低优先级的节点作为next,

         因此,任务优先级可以相同. 相同优先级先加入会先选中, 后加入排在同级最后


         这样不用类似uc/osii建立影射表. 也可以支持相同优先级任务调度.

供备忘, 参考...         
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2025-03-10 16:08:45

6

主题

131

回帖

666

积分

高级会员

积分
666
发表于 2025-2-25 15:43:31 | 显示全部楼层
Qx51 将于近期发布第一个公开测试版本V0.1.3beta
特点,系统服务不占用中断,因此系统是0中断延迟的
1.支持抢占式内核和临界区
2.任务切换不占用中断,而由一个特殊临界区运行的内核任务完成,只有系统时钟一个中断(可以NMI模式,也可以是普通中断)
3.接管中断,因此支持专用中断栈
4.任务栈不换入换出,因此任务切换比较快,任务上下文保存在xdata区,但任务栈无需搬移,也就是所谓的FiberStack模式
5.默认测试程序是LCD1602和ADCKEY加上串口0构成的前面板程序(STC8H8K64U)
6.后续有32位移植会支持更多IPC及组件
各位大佬欢迎指正,也欢迎随意提要求
软件是我的公司工作行为,因此在保留版权下,开源,欢迎学习或其他非商业使用,对中国公司商业使用小于1000片同样免费。
如果感兴趣的人超过3人,会提供相应技术讨论。
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 06:57 , Processed in 0.129040 second(s), 101 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表