2025-10-30

要学习一下riscv汇编函数调用参数选择的规则。

实现上下文切换

按照讲义里的提示做就可以,也就是在调用__am_irq_handle完成之后,先修改栈指针的位置再恢复现场。那么问题就是调用__am_irq_handle结束之后这个新的上下文指针在哪里呢?如果熟悉riscv汇编的话应该能知道这个返回值的位置,或者查看反汇编代码也能知道这个值就在寄存器$a0

RISC-V
<span class="line"><span style="color: #098658">80000010</span>
<span style="color: #000000"><schedule>:</span></span>
<span class="line"><span style="color: #000000">  ...</span></span>
<span class="line"><span style="color: #000000">  </span>
<span style="color: #795E26">sw</span>
<span style="color: #000000">    </span>
<span style="color: #001080">a5</span>
<span style="color: #000000">,</span>
<span style="color: #098658">0</span>
<span style="color: #000000">(</span>
<span style="color: #001080">a4</span>
<span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">  </span>
<span style="color: #795E26">ret</span></span>
RISC-V

所以只需要在调用__am_irq_handle下面添加如下代码就可以

RISC-V
<span class="line"><span style="color: #000000">  </span>
<span style="color: #795E26">call</span>
<span style="color: #000000">  __am_irq_handle</span></span>
<span class="line"><span style="color: #000000">  </span>
<span style="color: #008000"># Address of new context is in $a0</span></span>
<span class="line"><span style="color: #000000">  </span>
<span style="color: #795E26">mv</span>
<span style="color: #000000">    </span>
<span style="color: #001080">sp</span>
<span style="color: #000000">, </span>
<span style="color: #001080">a0</span></span>
<span class="line"><span style="color: #000000">  ...</span></span>
RISC-V

完成修改后可以发现成功实现了进程的切换,现象就是程序不停地输出问号。因为给f的参数还没有传递过去,需要在kcontext里传递这个参数,讲义提示可以查看ABI文档了解参数调用约定,其实到这里已经知道了第一个参数就在$a0里,或者从f的汇编代码里也可以获取这一信息

RISC-V
<span class="line"><span style="color: #098658">80000044</span>
<span style="color: #000000"> <f>:</span></span>
<span class="line"><span style="color: #000000">    ...</span></span>
<span class="line"><span style="color: #000000">    </span>
<span style="color: #795E26">li</span>
<span style="color: #000000">    </span>
<span style="color: #001080">a5</span>
<span style="color: #000000">,</span>
<span style="color: #098658">2</span></span>
<span class="line"><span style="color: #000000">    </span>
<span style="color: #795E26">mv</span>
<span style="color: #000000">    </span>
<span style="color: #001080">a4</span>
<span style="color: #000000">,</span>
<span style="color: #001080">a0</span></span>
<span class="line"><span style="color: #000000">    </span>
<span style="color: #795E26">bgeu</span>
<span style="color: #000000">  </span>
<span style="color: #001080">a5</span>
<span style="color: #000000">,</span>
<span style="color: #001080">a0</span>
<span style="color: #000000">,</span>
<span style="color: #098658">80000064</span>
<span style="color: #000000"> <f+</span>
<span style="color: #098658">0x20</span>
<span style="color: #000000">></span></span>
<span class="line"><span style="color: #000000">    ...</span></span>
RISC-V

结合f的c语言代码知道和2比较的就是函数的参数,也就是寄存器$a0,所以在kcontext函数将arg参数保存到$a0寄存器即可。完成后yield-os交替输出AB。

RT-Thread

RT-Thread这里一脸懵,完全不知道是什么,我甚至怀疑这是不是跑远了,为什么要学习RT-Thread?第一个任务是完成rt_hw_stack_init函数,但是我现在都不知道RT-Thread的上下文结构是什么,用AM中的Context吗?

关于地址对齐,常用的方法如下,x是需要做对齐的变量,align是对齐的宽度

C
<span class="line"><span style="color: #000000">  x = (x + align - </span>
<span style="color: #098658">1</span>
<span style="color: #000000">) & ~(align - </span>
<span style="color: #098658">1</span>
<span style="color: #000000">);</span></span>
C

RT-Thread

kcontext要求内核线程不能从entry返回,否则其行为是未定义的”,这是为什么呢?

我的理解是,kcontext注册的入口都是一些线程函数,以yield-os违例,操作系统不允许程序从线程函数结束,因为操作系统是在线程之上的,要结束也是操作系统控制结束,不能是线程控制结束

实在没啥思路,讲义也没怎么看明白,就到网上寻找其他人的经验,发现一个博客,写得很好

又顺带翻了一下作者其他的博客,发现他写得都很认真,能看出他对问题的思考,代码风格也不错,值得学习。又想起之前看到说要阅读优秀的代码,之前没什么体会,觉得看别人的代码要从何看起呢,现在来看,应该是一个问题先经过自己的思考然后动手去做,做完了碰到一些问题或者总觉得哪里不太对,这时候再去找一些优秀的设计去阅读,就能豁然开朗