ARM Cortex-M4 互斥锁。 DMB指令
ARM Cortex-M4 Mutex Lock. DMB Instruction
我阅读了以下文档:Barrier_Litmus_Tests_and_Cookbook by ARM。
获取mutex/semaphore的代码见7.2节。
Loop
LDREX R5, [R1] ; read lock
CMP R5, #0 ; check if 0
STREXEQ R5, R0, [R1] ; attempt to store new value
CMPEQ R5, #0 ; test if store suceeded
BNE Loop ; retry if not
DMB
LDREX 指令请求对内存地址进行独占访问。仅当处理器具有独占访问权限时,使用 STREX 的写入才会成功。
他们使用一条DMB指令来确保独占写入同步到所有处理器。
我有一个小问题。假设处理器独占访问内存地址并锁定它。一旦 STREX 指令完成,独占访问将被移除。其他处理器可以从现在开始访问此内存。然而,写入仍在处理器的缓存中,直到 DMB 完成。如果另一个处理器在第一个处理器已经锁定它但尚未同步到 RAM 时尝试获取对锁的访问权限,会发生什么情况。内存地址未独占锁定到第一个处理器,但写入未完成。
任何人都可以解释为什么这样做有效并且安全。我对此有疑问。
我认为你把它复杂化了。查看 amba/axi 规范(以及您在哪里找到多核 cortex-m4?)。 ldrex/strex 用于在多处理器芯片中跨处理器共享资源。一段时间以来,它们被错误地用于其他用途。不幸的是,ARM 在正确记录所有这些方面做得异常糟糕。
ldr独占的部分是processorid和地址(范围)保存在一个table中。当 strex 发生时,检查该地址(范围)的 processorid 是否与 EXOKAY 匹配,如果不匹配则执行存储,否则不匹配。 Strex 不清除任何东西,有趣的是,他们有这个 clrex 指令,我假设它将 processorid 设置为某个不会命中的值,或者取决于他们如何构建他们的 tables 他们释放了一个 table 条目。
我可能会在写完这篇文章后尝试这个,但你可以很容易地先 ldrex 然后 strex 然后 strex,相当肯定我已经在全尺寸手臂上做过 int,将在 cortex-m4 上尝试, strex 看看会发生什么。
在单处理器系统中,ldrex/strex 应该在 ARM 逻辑中工作,但芯片供应商不需要支持它,可能只是 return OKAY(而不是 EXOKAY)。 L1 当然也可能是 L2 是 arm 逻辑,超出了您进入芯片供应商的范围。 (cortex-ms 有 l2 吗?)。通常,您不必担心命中芯片供应商代码,您可以 运行 很长一段时间(如果不是无限期的话)都不知道这些,因为您将留在其中一个缓存中。例如,在 Linux 中禁用两个缓存是一个皇家 PITA,它们可能让它看起来像是一个编译时选项,但深入研究并看到现实。如果只有一个处理器,您如何获得不同的处理器 ID?
在多处理器芯片中,芯片供应商应该在缓存之外正确支持它,如果你甚至可以通过独占访问到达那里,ldrex/strex 如何正常使用,你很可能是在您的 L1 缓存中并且永远不会接触到芯片供应商提供的内容,但如果您在两者之间被打断并且您可能会被 L2 保存,则可能会发生这种情况。在这种情况下,芯片中有多个 processorid 是有道理的,因为处理器不止一个。
这很好
The Cortex-M4 processor implements a local exclusive monitor. The
local monitor within the processor has been constructed so that it
does not hold any physical address, but instead treats any access as
matching the address of the previous LDREX. This means that the
implemented exclusives reservation granule is the entire memory
address range.
m7 trm 说同样的话。
没有多个内核如何 could/would 一个生成不同的 ID?
文档使用术语 processorid 来指示正在使用哪个处理器。一个 cortex-m 有多少个处理器?也许它在其他地方使用不同的 string/name 进行了记录,但此时我不知道 cortex-m 中的 processorid 是如何生成的,作为一个单处理器是否不止一个?我无权访问内核,无法确定。
因此,即使逻辑不支持每个地址的独占访问,他们也没有说他们没有检查 processorid,他们只是认为对标记为共享的内存的所有 strex 访问都要根据最后一个 ldrex 的 processorid 检查与其地址无关。
编辑
PUT32(0x01000600,0x600);
PUT32(0x01000700,0x700);
PUT32(0x01000800,0x800);
CLREX();
hexstring(STREX(0x20000600,0x12345678));
hexstring(STREX(0x20000700,0x12345678));
hexstring(STREX(0x20000800,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000600,0x6666));
hexstring(STREX(0x20000700,0x12345678));
hexstring(STREX(0x20000800,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000700,0x7777));
hexstring(STREX(0x20000800,0x12345678));
hexstring(GET32(0x20000600));
hexstring(GET32(0x20000700));
hexstring(GET32(0x20000800));
CLREX();
hexstring(0xAABBCCDD);
hexstring(LDREX(0x20000600));
CLREX();
hexstring(STREX(0x20000600,0x2222));
hexstring(GET32(0x20000600));
生产
00000001
00000001
00000001
00000600 <-- ldrex
00000000 <-- strex pass
00000001 <-- strex fail
00000001
00006666
00000000
00000001
00006666
00007777
00000800
AABBCCDD
00006666
00000001
00006666
看起来他们在这里所做的是在 ldrex 独立于地址通过后的下一个 strex。所以使用你的术语 strex "clears the lock".
请注意,在 ldrex 和 strex 之间放置一个 clrex 确实会使 strex 失败。
不打同一个地址并不重要一个 ldrex 一个 strex
hexstring(LDREX(0x20000900));
hexstring(STREX(0x20000900,0x2222));
hexstring(STREX(0x20000900,0x2222));
3EEDCC1B
00000000
00000001
打开数据缓存并没有改变结果。
测试函数:
.thumb_func
.globl LDREX
LDREX:
ldrex r0,[r0]
bx lr
.thumb_func
.globl CLREX
CLREX:
clrex
bx lr
.thumb_func
.globl STREX
STREX:
strex r0,r1,[r0]
bx lr
不同于ARMS大哥:
CLREX();
hexstring(STREX(0x20000600,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000600,0x6666));
hexstring(LDREX(0x20000600));
PUT32(0x20000600,0x11);
hexstring(STREX(0x20000600,0x6666));
00000001
00000600
00000000
00006666
00000000
strex 在中间的非独占访问中幸存下来,至少根据您发布的文档,非独占存储应该破坏之前的 ldrex(在 armv7-a 上)。
注意上面是在 cortex-m4 r0p1 CPUID 0x410FC241
它是安全的,因为芯片设计者让它安全。 Test_and_Set 指令的全部要点将由操作系统用于信号量和互斥命令。在 multi-core/multi-processor 环境中,除了内置的汇编命令外,没有其他方法可以准确地实现此功能。
我阅读了以下文档:Barrier_Litmus_Tests_and_Cookbook by ARM。
获取mutex/semaphore的代码见7.2节。
Loop
LDREX R5, [R1] ; read lock
CMP R5, #0 ; check if 0
STREXEQ R5, R0, [R1] ; attempt to store new value
CMPEQ R5, #0 ; test if store suceeded
BNE Loop ; retry if not
DMB
LDREX 指令请求对内存地址进行独占访问。仅当处理器具有独占访问权限时,使用 STREX 的写入才会成功。 他们使用一条DMB指令来确保独占写入同步到所有处理器。
我有一个小问题。假设处理器独占访问内存地址并锁定它。一旦 STREX 指令完成,独占访问将被移除。其他处理器可以从现在开始访问此内存。然而,写入仍在处理器的缓存中,直到 DMB 完成。如果另一个处理器在第一个处理器已经锁定它但尚未同步到 RAM 时尝试获取对锁的访问权限,会发生什么情况。内存地址未独占锁定到第一个处理器,但写入未完成。
任何人都可以解释为什么这样做有效并且安全。我对此有疑问。
我认为你把它复杂化了。查看 amba/axi 规范(以及您在哪里找到多核 cortex-m4?)。 ldrex/strex 用于在多处理器芯片中跨处理器共享资源。一段时间以来,它们被错误地用于其他用途。不幸的是,ARM 在正确记录所有这些方面做得异常糟糕。
ldr独占的部分是processorid和地址(范围)保存在一个table中。当 strex 发生时,检查该地址(范围)的 processorid 是否与 EXOKAY 匹配,如果不匹配则执行存储,否则不匹配。 Strex 不清除任何东西,有趣的是,他们有这个 clrex 指令,我假设它将 processorid 设置为某个不会命中的值,或者取决于他们如何构建他们的 tables 他们释放了一个 table 条目。
我可能会在写完这篇文章后尝试这个,但你可以很容易地先 ldrex 然后 strex 然后 strex,相当肯定我已经在全尺寸手臂上做过 int,将在 cortex-m4 上尝试, strex 看看会发生什么。
在单处理器系统中,ldrex/strex 应该在 ARM 逻辑中工作,但芯片供应商不需要支持它,可能只是 return OKAY(而不是 EXOKAY)。 L1 当然也可能是 L2 是 arm 逻辑,超出了您进入芯片供应商的范围。 (cortex-ms 有 l2 吗?)。通常,您不必担心命中芯片供应商代码,您可以 运行 很长一段时间(如果不是无限期的话)都不知道这些,因为您将留在其中一个缓存中。例如,在 Linux 中禁用两个缓存是一个皇家 PITA,它们可能让它看起来像是一个编译时选项,但深入研究并看到现实。如果只有一个处理器,您如何获得不同的处理器 ID?
在多处理器芯片中,芯片供应商应该在缓存之外正确支持它,如果你甚至可以通过独占访问到达那里,ldrex/strex 如何正常使用,你很可能是在您的 L1 缓存中并且永远不会接触到芯片供应商提供的内容,但如果您在两者之间被打断并且您可能会被 L2 保存,则可能会发生这种情况。在这种情况下,芯片中有多个 processorid 是有道理的,因为处理器不止一个。
这很好
The Cortex-M4 processor implements a local exclusive monitor. The local monitor within the processor has been constructed so that it does not hold any physical address, but instead treats any access as matching the address of the previous LDREX. This means that the implemented exclusives reservation granule is the entire memory address range.
m7 trm 说同样的话。
没有多个内核如何 could/would 一个生成不同的 ID? 文档使用术语 processorid 来指示正在使用哪个处理器。一个 cortex-m 有多少个处理器?也许它在其他地方使用不同的 string/name 进行了记录,但此时我不知道 cortex-m 中的 processorid 是如何生成的,作为一个单处理器是否不止一个?我无权访问内核,无法确定。
因此,即使逻辑不支持每个地址的独占访问,他们也没有说他们没有检查 processorid,他们只是认为对标记为共享的内存的所有 strex 访问都要根据最后一个 ldrex 的 processorid 检查与其地址无关。
编辑
PUT32(0x01000600,0x600);
PUT32(0x01000700,0x700);
PUT32(0x01000800,0x800);
CLREX();
hexstring(STREX(0x20000600,0x12345678));
hexstring(STREX(0x20000700,0x12345678));
hexstring(STREX(0x20000800,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000600,0x6666));
hexstring(STREX(0x20000700,0x12345678));
hexstring(STREX(0x20000800,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000700,0x7777));
hexstring(STREX(0x20000800,0x12345678));
hexstring(GET32(0x20000600));
hexstring(GET32(0x20000700));
hexstring(GET32(0x20000800));
CLREX();
hexstring(0xAABBCCDD);
hexstring(LDREX(0x20000600));
CLREX();
hexstring(STREX(0x20000600,0x2222));
hexstring(GET32(0x20000600));
生产
00000001
00000001
00000001
00000600 <-- ldrex
00000000 <-- strex pass
00000001 <-- strex fail
00000001
00006666
00000000
00000001
00006666
00007777
00000800
AABBCCDD
00006666
00000001
00006666
看起来他们在这里所做的是在 ldrex 独立于地址通过后的下一个 strex。所以使用你的术语 strex "clears the lock".
请注意,在 ldrex 和 strex 之间放置一个 clrex 确实会使 strex 失败。
不打同一个地址并不重要一个 ldrex 一个 strex
hexstring(LDREX(0x20000900));
hexstring(STREX(0x20000900,0x2222));
hexstring(STREX(0x20000900,0x2222));
3EEDCC1B
00000000
00000001
打开数据缓存并没有改变结果。
测试函数:
.thumb_func
.globl LDREX
LDREX:
ldrex r0,[r0]
bx lr
.thumb_func
.globl CLREX
CLREX:
clrex
bx lr
.thumb_func
.globl STREX
STREX:
strex r0,r1,[r0]
bx lr
不同于ARMS大哥:
CLREX();
hexstring(STREX(0x20000600,0x12345678));
hexstring(LDREX(0x20000600));
hexstring(STREX(0x20000600,0x6666));
hexstring(LDREX(0x20000600));
PUT32(0x20000600,0x11);
hexstring(STREX(0x20000600,0x6666));
00000001
00000600
00000000
00006666
00000000
strex 在中间的非独占访问中幸存下来,至少根据您发布的文档,非独占存储应该破坏之前的 ldrex(在 armv7-a 上)。
注意上面是在 cortex-m4 r0p1 CPUID 0x410FC241
它是安全的,因为芯片设计者让它安全。 Test_and_Set 指令的全部要点将由操作系统用于信号量和互斥命令。在 multi-core/multi-processor 环境中,除了内置的汇编命令外,没有其他方法可以准确地实现此功能。