Навигатор
My RISC-V home
Индекс
Инструкции
расширения A
(атомарные операции)
RV32A
RV64A
|
Инструкции LR.W и LR.D
LR.W rd, (rs1)
LR.D rd, (rs1)
Набор команд: RV32A – с .W и
RV64A – с .D
Формат: R
Операнды: rs1 – адрес памяти, rd – регистр
Действие: rd := (rs1); «резервирует» прочитанные байты
(4 при .W и 8 при .D) в памяти, контролируя последующий доступ к ним; позднее парная с ней операция
SC.W/D проверит сохранность резервирования (т.е. чтобы значения в памяти за время между
LR и SC не изменились другим потоком или устройством)
Примечания
- LR = Load-Reserved.
- Работает «в паре» с
SC . Параметры LR и SC (адрес, число байтов) должны совпадать.
- В 64-битном режиме в команде с .W при записи в регистр rd в 32-битном слове расширяется знак.
- Адрес памяти в rs1 должен быть выровнен в соответствии с разрядностью операнда (т.е. делиться без остатка на 4 для .W и на 8 для .D). При нарушении возникает исключительная ситуация (exception). Возможность работы с невыровненными данными не предполагается.
- Общие правила для задания значений битов aq (acquire – захват) и rl (release – освобождение) такие же, как и в остальных инструкциях расширения A. Но во всех (немногочисленных) имеющихся примерах используются только нулевые значения битов aq и rl – см. пример 2.
- Инструкции с .W отличаются от таковых с .D значением в поле func3 (2 или 3 соответственно). Остальной код совпадает.
Пример 1
Инструкция LR.W x31, (x11) читает в x31 содержимое 32-битного слова по адресу из x11 , активируя при этом контроль за неизменностью в памяти прочитанного значения.
Код инструкции содержит следующие поля:
поле | разрядность | содержимое | примечание |
func5 | 5 битов | 00010 |
всегда |
aq | 1 бит | 0 | aq = 0 |
rl | 1 бит | 0 | rl = 0 |
rs2 | 5 битов | 00000 | всегда |
rs1 | 5 битов | 01011 | x11 |
func3 | 3 бита | 010 |
всегда |
rd | 5 битов | 11111 | x31 |
opcode | 7 битов | 0101111 |
всегда |
Итоговый код
00010 00 00000 01011 010 11111 01011112 = 10 05 AF AF16
Пример 2
Атомарная организация занятия ресурса с помощью пары команд LR/SC (как обычно, значение 1 – это занято, 0 – свободно; предполагается, что в x20 находится адрес памяти).
addi x12, x0, 1 # константу занятости 1 заносим в x12
again: lr.d x10, (x20) # считываем признак занятости для проверки
bne x10, x0, again # повторить, если кем-то уже занято (x10<>0)
sc.d x11, x12, (x20) # пытаемся занять
bne x11, x0, again # повторить, если не удалось (x11<>0)
# ...
sd x0, 0(x20) # освобождаем (заносим 0)
Пример 3
В известной книге Дэвида Паттерсона и Джона Хеннесси “Computer Organization and Design. The Hardware/Software Interface: RISC-V Edition” кроме приведенного выше примера 2 дана еще одна рекомендация по использованию пары LR/SC : между ними можно поместить несколько команд атомарной модификации прочитанного из памяти содержимого. Команд не должно быть много и допускаются не все инструкции, но так можно делать. Например, упомянуты примитивы синхронизации atomic compare and swap (CAS, близко к AMOSWAP ) и atomic fetch-and-increment (полный аналог AMOADD ).
Насколько я понимаю, следуя этой идее, атомарное увеличение счетчика можно написать так.
again: lr.d x6, (x10) # считываем значение из памяти
addi x6, x6, 1 # +1
sc.d x7, x6, (x10) # пытаемся записать результат
bne x7, x0, again # повторять, если не удалось (x7<>0)
Хотя, конечно, вариант с AMOADD выглядит проще и привлекательнее.
|