Понять ответ профессора на домашнее задание по сборке - PullRequest
3 голосов
/ 23 января 2020

мой проф опубликовал это как один из ответов на домашнее задание. Кто-нибудь может сломать это для меня? Я не понимаю, что он делает с CON1 - CON4 и что означают >> и 0x0FFF.

CON1:   EQU 6000
CON2:   EQU 6245
CON3:   EQU 10000
CON4:   EQU 10245
A:  DM 4                         ; DM is Define Memory

    addi    t1,  x0, A           ; t1 = &A

    lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
    addi    t0,  t0, CON1&0xFFF
    sd  t0,  0(t1)            // Cut and paste from last question of Quiz1
                                      // Blank line between groups of statements
    lui t0,  (CON2>>12) + ((CON2 & 0x0800)>>11)
    addi    t0,  t0, CON2&0xFFF
    sd  t0,  8(t1)

    lui t0,  (CON3>>12) + ((CON3 & 0x0800)>>11)
    addi    t0,  t0, CON3&0xFFF
    sd  t0,  16(t1)

    lui t0,  (CON4>>12) + ((CON4 & 0x0800)>>11)
    addi    t0,  t0, CON4&0xFFF
    sd  t0,  24(t1)
                                      // We need this to avoid the NO INSTRUCTION error
    ebreak x0, x0, 0              ; Suspend program.

Любая помощь будет признательна, спасибо. Мы используем RIS C -V

1 Ответ

3 голосов
/ 09 февраля 2020

В базовом наборе команд RIS C -V каждая инструкция кодируется в 32 бита. Это означает, что пространство для непосредственных операндов ограничено несколькими битами. Таким образом, чтобы получить большую константу в регистр (который с RV32G / RV64G также имеет ширину 32 или 64 бита), вам нужно разделить его и переместить части с несколькими инструкциями, то есть 2 с RV32G и до 8 с RV64G.

С 32-разрядной RIS C -V (RV32G) можно загружать более крупные константы с помощью немедленной загрузки сверху (lui) и добавления немедленного (* 1008) *) инструкция. Непосредственный операнд lui имеет ширину 20 бит, а addi допускает непосредственный операнд 12 бит. Таким образом, они достаточны для загрузки констант, которые используют до 32 бит.

lui знак расширяет его непосредственный операнд и сдвигает его влево на 12 бит и загружает результат в регистр назначения. Отсюда и его имя. addi также расширяет свой непосредственный операнд перед добавлением.

Таким образом, для RV32G, чтобы загрузить большую константу с lui, за которым следует addi, нужно взять старшие 20 бит, логически сдвиньте их вправо на 12 бит, так что 12-битное смещение влево на lui отменяется. Затем следует маскировка младших 12 бит, чтобы получить операнд для addi.

Этого достаточно, если addi не подписывает-расширяет свой непосредственный операнд. Если это так, поскольку старший бит установлен в 1, мы должны увеличить операнд lui так, чтобы лишние знаковые биты снова обнулялись при сложении.

Скажем, мы обозначаем старшую часть нашей константы x с h, нижняя часть с l, так как RIS C -V реализует два дополнения и арифметику c обертки при переполнении регистра, мы можем использовать модульную арифметику c, чтобы увидеть, что:

     h + l = x                             # taking register widths into account:
 => (h + l) % 2**32  = x % 2**32           # in case l is sign extended:
 => (h + l + e + c) % 2**32  = x % 2**32   # replace e with the additional sign bits:
<=> (h + l + 4294963200 + c) % 2**32  = x % 2**32     # eliminate c:
<=> (h + l + 4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + (4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + 0  = x % 2**32

Таким образом, мы должны добавить 1 к непосредственному операнду lui (который равен 4096 после смещения влево на 12 битов) тогда и только тогда, когда непосредственный операнд addi является расширенным знаком.

В вашем примере сборки >> обозначает сдвиг вправо, << сдвиг влево и & логическое и. Они используются для реализации описанного расщепления и арифметики c, например, в

 lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
 addi    t0,  t0, CON1&0xFFF

, где CON1 & 0x0800 маскирует 12-битный бит, то есть знаковый бит непосредственного операнда addi. Если он установлен, то ((CON1 & 0x0800)>>11) оценивается как 1 и, таким образом, удаляет лишние знаковые биты, добавленные следующей инструкцией addi. CON1&0xFFF маскирует младшие 12 бит.

В стандартной сборке RIS C -V всего этого утомительного управления битами можно избежать, просто используя псевдо немедленную загрузку (li) -инструкция, например:

li     t1, 6245

, которую ассемблер автоматически переводит в оптимальную последовательность команд (проверьте, например, с помощью objdump):

lui    t1, 0x2
addi   t1, t1,-1947

В качестве альтернативы, с GNU в качестве ассемблера также имеются директивы для разбиения операнда на верхнюю и нижнюю части:

lui    a1, %hi(6245)
addi   a1, a1, %lo(6245)

, что, возможно, также более читабельно, чем беспорядок в вашем фрагменте.

Это также работает с символами в GNU, как, например, :

.set CON2, 6245

li    a1, 6245

lui   a2, %hi(CON2)
addi  a2, a2, %lo(CON2)

li    a3, CON2

# => a1 == a2 == a3
...