riscv amo instructions
riscv amo(Atomic Memory Operation)指令
指令介绍
![image-20220802163048735](https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/image-20220802163048735.png)
amo指令主要用作多处理器同步,用来实现自旋锁,位操作。
These AMO instructions atomically load a data value from the address in rs1, place the value into register rd, apply a binary operator to the loaded value and the original value in rs2, then store the result back to the address in rs1.
amo指令从rs1所指向的地址addr中加载一个数据,把数据放到rd中,并把加载的数据和rs2中的数据进行运算,把结果放到rs1指向的addr中。
自旋锁
自旋锁的示例如下:
发现再两个不同版本的手册中,有略微的差别。
较新的版本[2]如下:
![image-20220802165059732](https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/image-20220802165059732.png)
- 先在寄存器
t0
中放入1
(li t0, 1
)。 - 然后把
a0
地址中的内容放到t1
中,a0
为锁的地址。 - 比较
t1
和0(bnez)
,如果不为0,说明锁已经被持有,回到again
。 - 把
t0
(1)放到a0
表示的地址中,含义为上锁,把原(a0)
放到t1
中。 - 同3,检查是否已经上锁了,如果上锁了,跳到again。
- 执行关键区代码。
- 解锁,把
x0
(0),的值交给(a0)
,把(a0)
的值交给x0
(遗弃)。
较旧版本[3]如下:
![image-20220802171746321](https://raw.githubusercontent.com/DavidZyy/PicBed/master/img/image-20220802171746321.png)
更加简洁一些,不知道为什么改掉了。
linux相关实现
bitops.h
分析linux riscv版本的bitops.h[4]。
set_bit
函数:
1 | static inline void __set_bit(int nr, volatile unsigned long *addr) |
宏展开为:
1 | __op_bit_ord(or, __NOP, nr, addr, ) |
继续展开为:
1 | __asm__ __volatile__ ( |
其中:
1 | /* flags数组每个元素为64bit,可以表示的flag数量为:数组大小*64 */ |
再看一个test_and_set_bit函数的实现:
1 | static inline int test_and_clear_bit(int nr, volatile unsigned long *addr) |
展开为:
1 | __test_and_op_bit_ord(and, __NOT, nr, addr, .aqrl) |
继续展开为:
1 | unsigned long __res, __mask; |
把addr[BIT_WORD(nr)]
旧的值保存在__res
中,然后clear
bit(与 ~mask
),然后返回旧的值与 __mask
。
bit spinlock的实现(简版)
1 | /* |
参考资料
[1] Multithreaded Application Synchronization pt. II (Mutual Exclusion Locks a'la RISC-V)
[2] The RISC-V Instruction Set Manual Volume I: Unprivileged ISA Document Version 20191213
[3] The RISC-V Instruction Set Manual Volume I: User-Level ISA Document Version 2.2