2

我对裸机编程非常陌生,以前从未接触过中断,但我一直在学习 RISC-V FE310-G002 SOC 驱动的开发板。

我一直在阅读有关 RISC-V WFI(等待中断)指令和手册的信息,听起来您不能依靠它来真正使内核休眠。相反,它只是建议系统可以暂停执行,并且应该将指令视为更像 NOP。但是,这对我来说似乎毫无用处。考虑以下 ASM 程序片段:

wfi_loop:
WFI
J wfi_loop

由于不能依赖 WFI,因此必须这样做。但是,在来自中断处理程序的 MRET 之后,您仍然会被困在循环中。因此,您必须以全局变量为条件,该变量的值在中断处理程序中更新。这似乎非常混乱。

此外,如果您的实现确实遵守 WFI 指令并且在 WFI 指令执行之前触发了中断,则整个内核将停止,直到触发其他中断,因为它将在 WFI 指令之前返回。

当没有工作要做时,该指令的唯一正确用法似乎是在内核调度程序中。但即便如此,我认为您永远不会希望从中断处理程序返回到此类代码,而是从头开始重新启动调度程序算法。但这也是一个问题,因为您将不得不以某种方式回滚堆栈等......

我一直在脑子里转来转去,似乎无法找到安全的使用方法。也许,如果您以原子方式使用 CSRRS 启用中断,然后立即像这样调用 WFI:

CSRRSI zero, mie, 0x80
wfi_loop:
WFI
J wfi_loop
NOP
NOP

然后确保在从中断处理程序调用 MRET 之前将 mepc 寄存器增加 8 个字节。在返回之前,还必须在中断处理程序内部的 mie 寄存器中再次禁用中断。这种解决方案只有在 WFI、J 和 NOP 都被编码为 4 字节指令时才是安全的,无论是否使用压缩指令。它还取决于程序计数器在被 CSRRSI 指令启用后,在可能触发中断之前到达 WFI 指令。然后,这将允许在代码中的安全位置触发中断,并以中断等待它的循环的方式返回。

我想我只是想了解我可以从硬件中得到什么行为,因此,如何正确调用中断并从中断返回并使用 WFI 指令?

4

2 回答 2

4

因此,您必须以全局变量为条件,该变量的值在中断处理程序中更新。

无论执行如何,您都必须这样做,wfi因为您不知道是什么事件导致了 hart 醒来。
您可能在执行时启用了n 个wfi中断,并且其中任何一个都可能已引发。

wfi是一种优化,它可以节省电力,直到发生某些事情。正如您所指出的,操作系统调度程序可能会发现自己处于没有线程可调度的情况(例如,它们都在等待 IO 或根本没有),在这种情况下,它必须执行类似的操作(具有所有必要的可见性和原子性语义):

while ( ! is_there_a_schedulable_thread());

那只是等待
但调度程序可以使用:

while ( ! is_there_a_schedulable_thread())
{
  __wfi();
}

在最坏的情况下,它就像紧密循环一样,在最好的情况下它会暂停 hart,直到发生外部中断(这意味着潜在的 IO 已完成,因此线程可能可以自由运行)。

即使在没有线程的情况下,每x微秒唤醒一次(由于计时器中断)也比浪费电源循环要好。

wfi如果您碰巧将所有工作都放在中断处理程序上(例如按下按钮或类似情况),那么嵌入编程也很有用。
在这种情况下,该main函数将永远循环,就像调度程序一样,但没有退出条件。
一条wfi指令将大大提高电池寿命。

但是你不能wfi在任何地方使用,或者你可能会发现自己在等待一个永远不会发生的中断(事实上,这是一个特权指令)。

将其视为与硬件协调的优化。

特别是,它的设计目的不是确保触发中断:

void wait_for_int(int int_num)
{
   //Leave only interrupt int_num enabled
   enable_only_int(int_num);
   __wfi();
   restore_interrupts();
}

考虑到 RISC-V 的特定实现,它可以以这种方式使用,但正如您从伪代码中看到的那样,它并不是那么方便。
禁用除一个中断之外的所有中断通常是操作系统无法承受的。
不过,嵌入式应用程序可以。

于 2020-08-19T11:22:13.987 回答
3

应该有一个用于空闲的任务/线程/进程,它应该看起来像你的第一段代码。

由于空闲线程被设置为具有最低优先级,如果空闲线程正在运行,则意味着没有其他线程可以运行,或者所有其他线程都被阻塞。

当一个中断发生解除了某个其他线程的阻塞时,中断服务例程应该恢复那个阻塞的线程而不是中断的空闲线程。

请注意,阻塞 IO 的线程本身也会被中断——它通过自己使用ecall. 该异常是对 IO 的请求,并导致该线程阻塞——在满足 IO 请求之前它无法恢复。

因此,在 IO 上被阻塞的线程就像被中断一样被挂起——并且时钟中断或 IO 中断能够恢复与立即中断的进程不同的进程,这将在空闲进程的情况下发生正在运行并且某个进程正在等待的事件发生。


我所做的是使用scratchcsr 指向当前正在运行的进程/线程的上下文块。在中断时,我保存(开始)服务中断所需的最少数量的寄存器。如果中断导致其他进程/线程变得可运行,那么当从中断恢复时,我会检查进程优先级,并且可能会选择上下文切换而不是恢复被中断的任何内容。如果我恢复中断的内容,它会快速恢复。为了切换上下文,我完成了保存中断线程的 CPU 上下文,然后恢复另一个进程/线程,切换scratch寄存器。

(对于嵌套中断,我不允许在恢复时进行上下文切换,但在保存当前上下文后的中断上,我确实scratch在重新启用更高优先级的中断之前将 csr 设置为上下文块的中断堆栈。此外,作为一个非常小的优化我们可以假设一个自定义编写的空闲线程除了保存/恢复它的 pc 之外不需要任何东西。)

于 2020-08-19T11:22:23.440 回答