再上一篇:第13章
上一篇:13.2 电源管理
主页
下一篇:13.4 自复位控制
再下一篇:第14章
文章列表

13.3 多处理机通信

《Cortex-M3 权威指南》,嵌入式处理器开发教程。

最让人意想不到的就是 CM3竟然还支持简单的多核功能!它上面有一个用于处理机之间同步任 务的简单通信接口。处理机有一个名为 TXEV(Transmit Event)的输出信号,用于发送信号给其 它处理机;还有一个名为 RXEV(ReceiveEvent)的输入信号,以接收从其它处理机发来的信号。 对于一个双核系统来说,事件通信的信号的连接可以如图 13.4所示:

图 13.4 双核处理系统间的事件信号连接 如上一小节所述,当处理机因为 WFE 而睡眠时,可以由外部事件——即 RXEV 唤醒。CM3 提供
了 SEV指令(Send EVent)。当执行该指令时,当事处理机就会在 TXEV上发送一个脉冲,从而可 以唤醒另外的睡眠中的处理机,从而实现同步,如图13.5所示。

图 13.5 双核之间使用事件信号来做同步任务
在使用 WFE同步任务时,要明白处理器也以被其它事件唤醒,比如中断和调试事件。所以在被 唤醒时,需要先检查是不是由同步事件信号唤醒的。使用 WFE同步任务的流程如图 13.6所示。

图 13.6 使用 WFE 同步任务模式图
通过使用 WFE,我们可以让两个处理机同步地配合完成一个任务(也可能会有少量时钟周期的 时差,这取决于器件的实现方式)。上图演示的是两台处理机的情况,事实上处理机的数目并没有 限制,但无论如何都必须有一个担当“主机”,用于发送同步事件。
当执行 WFE时,它首先检视本地事件锁存器。如果锁存器的值为零,则使内核睡眠;如果发现 锁住了先前的事件信号,则清零锁存器,并且取消此次睡眠,继续执行下一条指令。早先发生的异 常、执行的 SEV指令都可以置位锁存器。所以要注意,如果曾经执行过 SEV,则紧挨着的 WFE不会 使处理器睡眠,只是清除了锁存的值,处理器依然继续执行。

13.3.1 多机同步的深入讨论

事实上,同步问题远远要复杂得多。如果只是按图 13.6 那样单单使用 WFE,只能应付小儿科 的任务同步问题。在复杂的应用程序中,为正确地同步任务还需要附加的代码。正如上文所提到的, 处理器也以被其它事件唤醒,比如中断和调试事件。因此,内部的事件寄存器的当前状态常常是未 知的,故而不能保证在执行 WFE指令后就一定能进入睡眠。事实上,WFE常常在循环中使用(用于 降低系统的功耗),循环体中的代码检查状态,以判定需要同步的任务是否应该在 WFE后执行。
这种用法最典型示例就是多核系统中的信号量。在典型的情况下,需要一个系统级的互斥访问 监视器,在它的辅助下使用互斥访问指令来实现自旋锁(spin lock,熟悉 Linux的读者请给我你 们的微笑),以一轮一轮地尝试锁住共享的存储器或外设。自旋锁设施由 RTOS提供,通常由汇编语 言写成。RTOS提供类似 spin_lock()和 spin_unlock()的函数(如:Linux),而任务则可以使用 这两个函数来锁住所需的共享资源。
常规的自旋锁代码如下所示:

spin_lock ; 获取自旋锁的汇编示例代码,r0指向自旋锁变量

MOVS r2, #1 ; r2待会要写入自旋锁变量,表示资源已锁

spin_lock_loop

LDREX r1, [r0] CMP r1, #0

BNE spin_lock_loop ; 资源已被锁住,需重试

STREX r1, r2, [r0] ; 使用STREX指令尝试设置Lock_Variable为1

CMP r1, #0 ; 检查STREX指令的返回值

BNE spin_lock_loop ; STREX指令没有成功执行,重试

DMB ; 执行数据存储器隔离,以确保数据已落实到物理内存中。

BX LR ; 返回

在共享资源使用完毕后,需要释放自旋锁:

spin_unlock ; 释放自旋锁的汇编示例代码,r0指向自旋锁变量

MOVS r1, #0

DMB

;原文DMB在这里使用,疑似不妥

STR r1, [r0] ; Clear lock

DMB ; 执行数据存储器隔离,以确保数据已落实到物理内存中。

BX LR ; 返回

自旋锁的副作用,就是会当(获取锁的)处理机空闲时使(等待锁的)处理机白白空转,浪费 能源。因此,我们在上例的自旋锁中加入 WFE/SEV来解决这个问题:一方面,在尝试上锁的函数中, 一旦发现已上锁就执行 WFE;而在释放锁的函数中(在另一个处理机中执行此函数),释放后执行 SEV指令以唤醒所有尝试上锁的处理机。

spin_lock_with_WFE ; 使用WFE配合获取自旋锁的汇编示例代码,r0指向自旋锁变量

MOVS r2, #1 ; r2待会要写入自旋锁变量,表示资源已锁

spin_lock_loop

LDREX r1, [r0]

CBNZ r1, lock_is_set ; 如果r1!=0,则表示已上锁

STREX r1, r2, [r0] ; 使用STREX指令尝试设置Lock_Variable为1

CMP r1, #0 ; 检查STREX指令的返回值

BNE

spin_lock_loop

; STREX指令没有成功执行,重试

DMB

; 执行数据存储器隔离,以确保数据已落实到物理内存中。

BX

LR

; 返回

lock_is_set

WFE ; 资源已锁。等待使用资源的处理机释放锁后使用SEV发出信号 B spin_lock_loop ; 被唤醒,不管是不是被SEV唤醒的,先去尝试上锁 在共享资源使用完毕后,需要释放自旋锁:

在解开自旋锁的函数中,需要使用 SEV指令来唤醒其它所有需要该锁的处理机。

spin_unlock_with_SEV ; 释放自旋锁的汇编示例代码,r0指向自旋锁变量

MOVS r1, #0

DMB ;原文DMB在这里使用,疑似不妥

STR

r1,

[r0]

; Clear lock

DMB

SEV

; 执行数据存储器隔离,以确保数据已落实到物理内存中。

BX

LR

; Return

通过在信号量代码中配合使用事件通信接口,就可以在使用自旋锁尝试获取共享资源时消除不
必要的功耗。类似的技术也可以用于创建消息队列等其它任务同步设施。

译者添加:自旋锁不是谁想用谁就能用的,必须分场合。如果是同一个处理机内的多个任务需要某共享资源, 且在其它处理机上没有需要此资源的任务,就不得使用带 WFE的自旋锁,因为在执行 WFE后,该处理机已经睡眠了, 无法再执行其它指令,更不要说调度其它任务让它调用 spin_unlock_with_SEV 了。此时又没有“外力”,因此就 很可能要“长眠”了!进一步地,单机场合下不得使用自旋锁。因为自旋锁可能导致死循环:优先级最高的任务如 果使用自旋锁未果,则在按优先级调度的 RTOS 中,如果没有反优先级倒转机制,就会使最高优先级的任务永远死 循环,CPU利用率100%,却再也执行不了其它任务!

在大多数 CM3系统中,会只使用一个内核。此时,常常是把 RXEV脚拉低,或者连接到其它可

以产生事件的外设上。

Cortex-M3 r2p0修订版新增

请注意:当使能了 SLEEPONEXIT特性时,CM3在异常退出后不经过执行 WFI/WFE就会 进入睡眠模式。因此当需要执行睡眠时,在正常的使用场合下,要在 WFI/WFE指令(得 到执行)之前就使能 SLEEPONEXIT。
在 Cortex-M3修订版 2(已于 2008年出品)中,又添加了新的特性以支持低功耗。 从软件的立场上来看,WFI/WFE 依然故我。但在硬件上,修订版的深度睡眠模式则睡 得更深:允许送往处理器内核的时钟信号停止。那这么一来怎样唤醒内核呢?原来,修 订版 2的内核新增了一个独立的单元,称作“唤醒中断控制器”。有了它,处理器内核 可以在进入掉电模式时,把处理器状态信息存储到特殊的逻辑小室(cells)中,从而 更狠地降低空闲时的功耗。
要使用新的掉电模式,还需要一个外部电源管理单元来配合,后者用于控制上电序 列和掉电序列。该单元由芯片供应商提供,在使用掉电特性前可能还要编程它,因此需 要参考芯片供应商提供的技术文档。关于掉电特性,还有两点要注意的。首先,是它会 关掉送往 SysTick 定时器的时钟。第 2,当连接了一个调试器时,为了使它能够正常 地访问调试寄存器,会自动除能这个掉电特性。