04场 场景延伸:I/O 设备与归约操作的加速器 原子性不仅限于核间互斥。在 I/O 设备通信中,给寄存器进行原子读写,就能把驱动的忙等待逻辑给简化,提升吞吐。高效归约算法利用 AMO 的“读—改—写”特性,能在多核间并行累加、求和,从而减少汇总开销。 这次04场的话题主要围绕原子指令扩展展开。RISC-V 把原子操作从原本的指令集里拆了出来,做成了独立扩展 'A'。这个扩展里只有两套机制,一个是 AMO(Atomic Memory Operations),另一个是 LR/SC(Load Reserved / Store Conditional)。拆分以后,芯片设计者就可以按需裁剪了。不管是高性能场景还是小内存需求,都能照顾到。真正做到了指令可生长。 AMO 指令的核心模型是一条流水线完成“读—改—写”。这过程中总线被锁定了,别的核就插不上手了。一条 RV32A 的 AMO 指令就能搞定:把 rs1 指向的内存值载入 rd,用 rs2 的值对 rd 进行位运算,再把结果写回原地址并释放总线。 LR/SC 给多核疯狂抢锁的情况提供了一个解决办法。当总线被长期锁定时,AMO 的三步锁就成了瓶颈。LR/SC 是分两步走:先占位再写回。LR 指令把 rs1 的值载入 rd,在内存地址上设置保留标记;SC 指令尝试把 rs2 的值写入 rs1。如果地址已经被锁定了,就直接返回失败(rd=1),成功的话 rd=0。无论成功还是失败,SC 都会清空当前 hart 的所有保留标记。 这次01场讨论的是原子指令为何被拆成独立扩展。RISC-V 把原本打包在指令集里的原子操作拆成独立扩展 'A',里面只有 AMO 和 LR/SC 两种机制。拆分以后设计芯片的时候可以按需选择。既满足高性能需求又照顾到小内存情况。 这个05场总结一下:RISC-V 把原子操作拆成独立扩展后用 AMO 做轻量级位操作,用 LR/SC 解决高频抢锁问题。这样既保证了多核一致性又避免了总线长时间堵塞。对于系统级芯片来说选择合适的原子指令策略就等于选择了性能和功耗之间的平衡点。 AMO 可以用来实现轻量级自旋锁。下面这段伪代码演示了这个过程:给 t0 赋值为 1(锁值 1),把 t1 赋值为 [a0] 的值(读取锁状态),如果 t1 不等于 0 就重试(bnez t1, again),执行 sc t0, [a0] 就把 1 写入并等待释放总线(上锁),如果 rd 不等于 0 再次重试(bnez rd, again),临界区开始执行代码...最后执行 sc x0, [a0] 解锁并清除保留标记。全程都不用调用昂贵的系统调用,特别适合嵌入式场景。 这次02场介绍了 AMO 的核心模型:“读—改—写”一条流水线完成。总线被锁定其他核插不上手。RV32A 的 AMO 指令能完成把 rs1 指向的内存值载入 rd,用 rs2 对 rd 进行位运算,再写回原地址并释放总线。 这次03场讨论了 LR/SC 如何解决多核抢锁问题。LR 指令载入旧值并设置保留标记;SC 指令尝试写入新值并清除标记。如果写入失败返回失败标记 rd=1;成功则返回 rd=0。无论成功还是失败都会清空所有保留标记避免死锁。 示例伪代码如下:给 t1 赋值为 [a0] 的值(读取旧值),如果 t1 等于 a1 就执行交换(beq t1, a1, swap),否则跳转到 loop 重试;交换成功后的代码放在 swap 标签下;loop 标签下是重试逻辑;执行 sc t2, [a0] 尝试写入新值并清标记;如果 rd 等于 0 表示写入成功跳转到 done 退出临界区;否则跳转到 loop 重试;done 标签下是退出临界区的代码。 这次05场总结一下:RISC-V 把原子操作拆成独立扩展后用 AMO 做轻量级位操作,用 LR/SC 解决高频抢锁问题。这样既保证了多核一致性又避免了总线长时间堵塞。对于系统级芯片来说选择合适的原子指令策略就等于选择了性能和功耗之间的平衡点。 RISC-V B 位扩展提供了复杂位控制功能,但 AMO 的精简版本只需一条指令就能完成“读—改—写”闭环。对于只需要单比特锁控的 MCU 外设来说能大幅精简固件体积。