- Включает RV32I: регистры, ABI и управление потоком с помощью e-call.
- Практика с Юпитером и упражнения: негатив, факторы, цепочки, рекурсия.
- Мастер кросс-инструментария, скрипт линковка и отладка с помощью objdump.
Если вам интересен ассемблер и вы считаете, что 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, например в образовательные симуляторы с собственным сервисом Интернета вещей, измените номер вызова и регистры в соответствии с документацией по окружающей среде.
Начальные упражнения, которые помогут вам освоиться с условными операторами, циклами и памятью
Типичная разминка — определить, является ли целое число отрицательным. Вы можете вернуть 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).
Для обхода целочисленных массивов используйте 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.
Кросс-инструментальная цепочка, связывание и отладка
Для генерации двоичных файлов 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, которая позволяет вам мигать светодиодом с помощью собственного процессора.
Страстный писатель о мире байтов и технологий в целом. Мне нравится делиться своими знаниями в письменной форме, и именно этим я и займусь в этом блоге: покажу вам все самое интересное о гаджетах, программном обеспечении, оборудовании, технологических тенденциях и многом другом. Моя цель — помочь вам ориентироваться в цифровом мире простым и интересным способом.