Что нужно для начала программирования на ассемблере RISC-V

Последнее обновление: 08/10/2025
Автор: Исаак
  • Включает RV32I: регистры, ABI и управление потоком с помощью e-call.
  • Практика с Юпитером и упражнения: негатив, факторы, цепочки, рекурсия.
  • Мастер кросс-инструментария, скрипт линковка и отладка с помощью objdump.

Ассемблер RISC-V: требования и начало работы

Если вам интересен ассемблер и вы считаете, что RISC-V — это то, что вам нужно, то вы попали по адресу. Начало работы с ASM на RISC-V более доступно, чем кажется Если вы понимаете инструменты, модель programación и некоторые ключевые правила архитектуры.

В следующих строках я объединил лучшее из нескольких источников: практики с симуляторами типа «Юпитер», условности базового репертуара RV32I, примеры циклов и рекурсии, системные вызовы и даже взгляд на проектирование ЦП RISC-V на VHDL (с АЛУ, управлением памятью и конечным автоматом), а также обзор кросс-инструментальных цепочек и скриптов связывания.

Что такое ассемблер RISC-V и чем он отличается от машинного языка?

Хотя оба они прикреплены к аппаратные средства, Машинный язык — чисто двоичный (единицы и нули) которые процессор интерпретирует напрямую, в то время как ассемблер использует мнемонику и Символы более читабельно, чем ассемблер, затем переводится в двоичный код.

RISC-V определяет открытую ISA с очень чистым базовым репертуаром. Профиль RV32I (32-бит) включает 39 пользовательских инструкций. с замечательной ортогональностью, разделяющей доступ к памяти от чистых вычислений, и с превосходной поддержкой в ​​GCC/LLVM.

Записи, соглашения и точка входа

В RV32I у вас есть 32 регистра общего назначения (x0–x31) 32-битное значение; x0 всегда читается как 0 и не может быть записано. Псевдонимы, такие как a0–a7 (аргументы), t0–t6 (временные) или s0–s11 ​​(сохранённые), также полезны для отслеживания ABI.

В зависимости от среды или симулятора программа может запускаться с определенной метки. В Jupiter программы начинаются с глобального тега __start., который необходимо объявить видимым (например, с помощью .globl), чтобы обозначить точку входа.

Лас- теги заканчиваются двоеточием, вы можете разместить только одну инструкцию в каждой строке, а комментарии могут начинаться с # или ;, поэтому ассемблер их игнорирует.

Инструменты и симуляторы: Jupiter и базовый рабочий процесс

Чтобы практиковать без осложнений, у вас есть Симулятор/ассемблер Юпитера, графический инструмент, созданный по образцу SPIM/MARS/VENUS, который упрощает редактирование, сборку и выполнение в единой среде.

В Jupiter вы можете создавать, редактировать и удалять файлы на вкладке Редактор. После сохранения соберите с помощью F3 и запустите отлаживать поток инструкций одна за другой, используя представления регистров и памяти для понимания состояния машины.

Программы должны заканчиваться призывом к окружающей среде: выход из настройки вызова a0 с кодом 10 (выход). В RISC-V повторные вызовы эквивалентны системным вызовам или прерываниям в среде/системе.

Минимальная структура программы и системные вызовы

Типичная структура академических примеров определяет отправную точку, выполняет работу и завершается повторным вызовом. Аргументы отзыва обычно перемещаются в интервале a0–a2 и селектор услуг в a7, в зависимости от среды.

В одном Linux Например, в RISC-V можно выполнить печать с помощью системного вызова write и выйти с помощью соответствующего кода. Для записи a0 (fd), a1 (буфер), a2 (длина) и a7 используются с номером сервиса. Наконец, a0 устанавливается на код возврата, а a7 — на номер выхода.

# Ejemplo mínimo (Linux RISC-V) para escribir y salir
.global _start
_start:
  addi a0, x0, 1        # fd = 1 (stdout)
  la   a1, msg          # a1 = &msg
  addi a2, x0, 12       # a2 = longitud
  addi a7, x0, 64       # write
  ecall

  addi a0, x0, 0        # return code
  addi a7, x0, 93       # exit
  ecall

.data
msg: .ascii "Hola mundo\n"

Если вы работаете вне Linux, например в образовательные симуляторы с собственным сервисом Интернета вещей, измените номер вызова и регистры в соответствии с документацией по окружающей среде.

  Получите ISO-файлы Windows 8.1 [Настройка USB и DVD]

Начальные упражнения, которые помогут вам освоиться с условными операторами, циклами и памятью

Типичная разминка — определить, является ли целое число отрицательным. Вы можете вернуть 0, если число положительное, и 1, если число отрицательное.; в RV32I сравнение с 0 и установка на меньше чем решают проблему одной хорошо продуманной инструкцией.

Еще одно очень полезное упражнение — перечисление множителей числа: проходит от 1 до n, печатает делители и возвращает, сколько их былоВы попрактикуетесь в условных переходах, делении (или повторном вычитании) и циклах со сложением и сравнением.

Работа со строками заставляет вас управлять памятью: Обнаружить каждый символ строки в памяти и на месте преобразовать строчные буквы в заглавные Если они соответствуют диапазону ASCII. После завершения возвращается исходный адрес строки.

Циклы, функции и рекурсия: факториал, Фибоначчи и Ханойская башня

При проектировании циклов подумайте о трех блоках: условии, теле и шаге. С beq/bne/bge и безусловными переходами jal/j while/for строятся без таинственности, полагаясь на дополнения и сравнения.

.text
.globl __start
__start:
  li t0, 0        # i
  li t1, 10       # max
cond:
  bge t0, t1, end # si i >= max, salta
  # cuerpo: usar a0/a1 segun servicio IO del entorno
  addi t0, t0, 1  # i++
  j cond
end:
  li a0, 10
  ecall

При вызовах функций соблюдайте ABI: сохранить, если вы собираетесь связать больше звонков, сохраняет s0–s11 ​​​​если вы их изменяете, и использует стек с sp, перемещающимся кратно слову.

Факториал — это классическая рекурсия: базовый случай n==0 возвращает 1; в противном случае вызвать factorial(n-1) и умножить на n. Защитить ra и регистры, сохранённые в стеке до вызова, и восстановить их при возврате.

factorial:
  beq a0, x0, base
  addi sp, sp, -8
  sw   ra, 4(sp)
  sw   s0, 0(sp)
  mv   s0, a0
  addi a0, a0, -1
  jal  factorial
  mul  a0, a0, s0
  lw   s0, 0(sp)
  lw   ra, 4(sp)
  addi sp, sp, 8
  jr   ra
base:
  li a0, 1
  jr ra

Фибоначчи полезен для практики обоих рекурсия с двумя вызовами как эффективную итеративную версию с переменными-аккумуляторами. А если вам нужна задача по управлению потоком и параметрами, переведите решение на ассемблер. Ханойские башни с четырьмя аргументами: диски, источник, пункт назначения и вспомогательная башня; он учитывает порядок вызовов и отображает каждое движение.

Доступ к памяти, массивы и работа со строками

В RISC-V доступ к памяти осуществляется с помощью загрузки/сохранения: lw/sw для слов, lh/sh для полуслов и lb/sb для байтов, со знаковыми или незнаковыми вариантами в зарядах (lb против lbu, lh против lhu).

  Project Zomboid на Android: можно ли играть в него на мобильном устройстве?

Для обхода целочисленных массивов используйте 4-байтовые смещения на индекс; для текстовых строк продвигается байт за байтом, пока не найдет терминатор Если того требует соглашение (например, \0). Не забудьте сохранить базовые адреса и обрабатывать указатели с помощью addi/auipc/la по мере необходимости.

Проектирование процессора RV32I с нуля: общий обзор

Если вы хотите перейти к кремнию, образовательный проект создаст Процессор RV32I в VHDL, синтезируемый в ПЛИС Диапазон ниже среднего. Включает ПЗУ программ, ОЗУ данных и простой вывод GPIO для включения светодиода.

Ядро реализует базовый репертуар (без расширений M/A/C или CSR), использует 32-битную адресную шину и обеспечивает доступ к 8-/16-/32-битной знаковой памяти при необходимости. Конструкция чётко разделяет регистры, АЛУ, контроллер памяти и конечный автомат.

АЛУ, сдвиги и идея «отложенной загрузки»

АЛУ описывается в сочетании с такими операциями, как сложение, вычитание, XOR, OR, AND, сравнения (знаковые и беззнаковые) и логические/арифметические сдвиги.

Для экономии LUT в ПЛИС реализованы многобитные сдвиги итерации 1-битных сдвигов, контролируемых конечным автоматом: вы потребляете несколько циклов, но уменьшаете логические ресурсы.

В синхронных цепях изменения наблюдаются на фронтах тактовых импульсов. Концепция «отложенной загрузки» подразумевает, что выбор мультиплексора влияет на регистр в следующем цикле., ключевой аспект при проектировании конечного автомата «выборка-декодирование-исполнение».

Контроллер памяти и карта: ПЗУ, ОЗУ и GPIO

Блок памяти объединяет ПЗУ и ОЗУ в единое пространство, упрощение интерфейса процессораКонтроллер получает AddressIn (32 бита), DataIn, ширину (байт/половина/слово), сигнал расширения знака, WE (чтение/запись) и Start для инициирования транзакций.

Когда операция закончится, ReadyOut устанавливается в 1 и, если он был прочитанDataOut содержит данные (с расширением знака по запросу). Если данные были записаны, они остаются в оперативной памяти.

Пример практической карты: ПЗУ от 0x0000 до 0x0FFF, байт GPIO по адресу 0x1000 (бит 0 на выводе) и ОЗУ от 0x1001 до 0x1FFFС его помощью можно сделать мигалку, записав и переключив выходной бит.

Регистры, мультиплексоры и конечные автоматы

Процессор определяет 32 регистра общего назначения, созданных с помощью массивов в VHDL, с декодер чтобы выбрать место назначения записи из АЛУ и сохранить остальное.

Мультиплексоры управляют входами АЛУ (операндами и операциями), сигналы к контроллеру памяти (ширина, адрес, управление запуском и чтение/запись) и специальные регистры: PC, IR и вспомогательный счетчик для итеративных сдвигов.

Государственная машина начинается с сброс, извлекает инструкцию, на которую указывает PC (чтение 4 байт), по готовности загружается в IR и передается в узлы выполнения: АЛУ (одна инструкция за 1 цикл, за исключением сдвигов), загрузка/сохранение, переходы и переходы, а также специальные инструкции, такие как ebreak.

  Как настроить Bluetooth в Windows 10

Кросс-инструментальная цепочка, связывание и отладка

Для генерации двоичных файлов RV32I используйте Cross GCC (цель riscv32-none-elf)Вы компилируете исходные коды C/C++/ASM, связываете их со скриптом, определяющим карту памяти, и преобразуете выходные данные в форму, которую ожидает ваша ПЗУ/ПЛИС.

Простой скрипт-хук может разместить .текст в ПЗУ с 0x0000 и .data в ОЗУ с адреса 0x1004 (если адреса 0x1000–0x1003 заняты регистрами GPIO). Процедура запуска может быть «голой» и помещена указатель стека в конце ОЗУ (например, 0x1FFC) перед вызовом main.

/* Mapa simple
 * ROM: 0x00000000 - 0x00000FFF
 * GPIO: 0x00001000 - 0x00001003
 * RAM: 0x00001004 - 0x00001FFF
 */
SECTIONS {
  . = 0x00000000;
  .text : { *(.startup) *(.text) *(.text.*) *(.rodata*) }
  . = 0x00001004;
  .data : { *(.data) *(.data.*) }
}

С помощью riscv32-none-elf-objdump вы можете разобрать ELF и проверить адреса; например, вы увидите Загрузка по адресу 0x00000000 с инструкциями типа lui/addi/jal и переходом в ваш основной код. Для моделирования VHDL GHDL генерирует трассировки, которые можно открыть с помощью GtkWave.

После проверки в процессе моделирования перенесите проект на ПЛИС (Quartus или другой инструментарий). Если ОЗУ выведено как внутренние блоки и код ясен RTL, вы должны синтезировать без сюрпризов, даже на ветеранских устройствах.

Практические напоминания и типичные ошибки на начальном этапе

Не забывай что x0 всегда равен нулю; запись в него не имеет никакого эффекта, а чтение возвращает 0. Используйте это в своих интересах при дополнениях, сравнениях и очистке реестра.

При реализации функций, сохранить записи ra и sN, которые вы изменяетеи управляет стеком с помощью сложений/вычитаний, выровненных по словам, к sp. При возврате он восстанавливается в обратном порядке и переходит с помощью jr ra.

В симуляторах типа Юпитера проверьте, что __start является глобальным и завершается с помощью ecall Правильно (a0=10 для выхода). Если что-то не запускается, проверьте метку, глобальность и перекомпилируйте (F3).

В упражнениях с ИО, соблюдать протокол охраны окружающей среды: какие регистры содержат параметры, номер сервиса и ожидается ли адрес или немедленное значение. Используйте документацию симулятора или операционной системы.

Благодаря понятной базе ISA (RV32I, регистры и ABI), удобному симулятору типа Jupiter и увеличивающемуся количеству примеров (отрицательные, множители, заглавные буквы, циклы, факториал, Фибоначчи и Ханой) ассемблер RISC-V перестает быть стеной и становится лакомым кусочком для понимания того, как думает центральный процессор. А если вы осмелитесь перейти на VHDL, вы увидите, как взаимодействуют АЛУ, память и управление.: от выборки инструкций и ленивой загрузки до интерфейсов памяти и карты с ПЗУ, ОЗУ и GPIO, которая позволяет вам мигать светодиодом с помощью собственного процессора.

Лучшие программы для программирования
Теме статьи:
7 лучших программ для программирования